From f0da4c9b6e7949436bc3bf56ff76a9b9f6c54c5f Mon Sep 17 00:00:00 2001 From: Chi Zhang Date: Mon, 22 Jun 2020 08:06:26 -0700 Subject: [PATCH] add --delete option for the perf-tests tool (#1432) --- testutils/clustermanager/perf-tests/main.go | 13 ++- .../clustermanager/perf-tests/pkg/cluster.go | 13 +++ .../perf-tests/pkg/cluster_test.go | 97 +++++++++++++++++++ 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/testutils/clustermanager/perf-tests/main.go b/testutils/clustermanager/perf-tests/main.go index 70b0e88ba..f8084c078 100644 --- a/testutils/clustermanager/perf-tests/main.go +++ b/testutils/clustermanager/perf-tests/main.go @@ -27,6 +27,7 @@ import ( var ( isRecreate bool isReconcile bool + isDelete bool gcpProjectName string repoName string benchmarkRootFolder string @@ -40,10 +41,11 @@ func main() { flag.StringVar(&benchmarkRootFolder, "benchmark-root", "", "root folder of the benchmarks") flag.BoolVar(&isRecreate, "recreate", false, "is recreate operation or not") flag.BoolVar(&isReconcile, "reconcile", false, "is reconcile operation or not") + flag.BoolVar(&isDelete, "delete", false, "is delete operation or not") flag.Parse() - if isRecreate && isReconcile { - log.Fatal("Only one operation can be specified, either recreate or reconcile") + if (isRecreate && isReconcile) || (isRecreate && isDelete) || (isReconcile && isDelete) { + log.Fatal("--recreate, --reconcile and --delete are mutually exclusive") } client, err := testPkg.NewClient(gkeEnvironment) @@ -61,7 +63,12 @@ func main() { log.Fatalf("Failed reconciling clusters for repo %q: %v", repoName, err) } log.Printf("Done with reconciling clusters for repo %q", repoName) + case isDelete: + if err := client.DeleteClusters(gcpProjectName, repoName, benchmarkRootFolder); err != nil { + log.Fatalf("Failed deleting clusters for repo %q: %v", repoName, err) + } + log.Printf("Done with deleting clusters for repo %q", repoName) default: - log.Fatal("One operation must be specified, either recreate or reconcile") + log.Fatal("One operation must be specified, either recreate, reconcile or delete") } } diff --git a/testutils/clustermanager/perf-tests/pkg/cluster.go b/testutils/clustermanager/perf-tests/pkg/cluster.go index a388c5d75..90f0ccfc5 100644 --- a/testutils/clustermanager/perf-tests/pkg/cluster.go +++ b/testutils/clustermanager/perf-tests/pkg/cluster.go @@ -103,6 +103,19 @@ func (gc *gkeClient) ReconcileClusters(gcpProject, repo, benchmarkRoot string) e return gc.processClusters(gcpProject, repo, benchmarkRoot, handleExistingCluster, handleNewClusterConfig) } +// DeleteClusters will delete all existing clusters. +func (gc *gkeClient) DeleteClusters(gcpProject, repo, benchmarkRoot string) error { + handleExistingCluster := func(cluster container.Cluster, configExists bool, config ClusterConfig) error { + // retain the cluster, if the cluster config is unchanged + return gc.deleteClusterWithRetries(gcpProject, cluster) + } + handleNewClusterConfig := func(clusterName string, clusterConfig ClusterConfig) error { + // do nothing + return nil + } + return gc.processClusters(gcpProject, repo, benchmarkRoot, handleExistingCluster, handleNewClusterConfig) +} + // processClusters will process existing clusters and configs for new clusters, // with the corresponding functions provided by callers. func (gc *gkeClient) processClusters( diff --git a/testutils/clustermanager/perf-tests/pkg/cluster_test.go b/testutils/clustermanager/perf-tests/pkg/cluster_test.go index 8b221a917..e6580f10f 100644 --- a/testutils/clustermanager/perf-tests/pkg/cluster_test.go +++ b/testutils/clustermanager/perf-tests/pkg/cluster_test.go @@ -23,6 +23,7 @@ import ( "github.com/google/go-cmp/cmp" container "google.golang.org/api/container/v1beta1" + "knative.dev/pkg/test/gke" gkeFake "knative.dev/pkg/test/gke/fake" ) @@ -291,7 +292,103 @@ func TestReconcileClusters(t *testing.T) { tc.testName, fakeProject, fakeRepository, tc.benchmarkRoot, diff) } } +} +func TestDeleteClusters(t *testing.T) { + testCases := []struct { + testName string + benchmarkRoot string + precreatedClusters map[string]ClusterConfig + expectedClusters map[string]ClusterConfig + }{ + // nothing will be done if there is no cluster at the beginning + { + testName: "all related clusters will be deleted", + benchmarkRoot: testBenchmarkRoot, + precreatedClusters: make(map[string]ClusterConfig), + expectedClusters: make(map[string]ClusterConfig), + }, + // all clusters will be created if there is no cluster at the beginning + { + testName: "all related clusters will be deleted", + benchmarkRoot: testBenchmarkRoot, + precreatedClusters: map[string]ClusterConfig{ + clusterNameForBenchmark("test-benchmark1", fakeRepository): { + Location: "us-central1", + NodeCount: 2, + NodeType: "n1-standard-4", + }, + clusterNameForBenchmark("test-benchmark2", fakeRepository): { + Location: "us-west1", + NodeCount: 2, + NodeType: "n1-standard-8", + }, + }, + expectedClusters: make(map[string]ClusterConfig), + }, + // clusters that do not belong to this repo will not be touched + { + testName: "clusters that do not belong to this repo will not be touched", + benchmarkRoot: testBenchmarkRoot, + precreatedClusters: map[string]ClusterConfig{ + clusterNameForBenchmark("test-benchmark1", fakeRepository): { + Location: "us-central1", + NodeCount: 2, + NodeType: "n1-standard-4", + }, + "unrelated-cluster": { + Location: "us-central1", + NodeCount: 3, + NodeType: "n1-standard-4", + }, + }, + expectedClusters: map[string]ClusterConfig{ + "unrelated-cluster": { + Location: "us-central1", + NodeCount: 3, + NodeType: "n1-standard-4", + }, + }, + }, + } + + for _, tc := range testCases { + client := setupFakeGKEClient() + for name, config := range tc.precreatedClusters { + region, zone := gke.RegionZoneFromLoc(config.Location) + var addons []string + if strings.TrimSpace(config.Addons) != "" { + addons = strings.Split(config.Addons, ",") + } + req := &gke.Request{ + ClusterName: name, + MinNodes: config.NodeCount, + MaxNodes: config.NodeCount, + NodeType: config.NodeType, + Addons: addons, + } + creq, _ := gke.NewCreateClusterRequest(req) + client.ops.CreateCluster(fakeProject, region, zone, creq) + } + err := client.DeleteClusters(fakeProject, fakeRepository, testBenchmarkRoot) + fmt.Println(err) + + clusters, _ := client.ops.ListClustersInProject(fakeProject) + actual := make(map[string]ClusterConfig) + for _, cluster := range clusters { + actual[cluster.Name] = ClusterConfig{ + Location: cluster.Location, + NodeCount: cluster.NodePools[0].Autoscaling.MaxNodeCount, + NodeType: cluster.NodePools[0].Config.MachineType, + Addons: getAddonsForCluster(cluster), + } + } + + if diff := cmp.Diff(tc.expectedClusters, actual); diff != "" { + t.Fatalf("Test %q fails, DeleteClusters(%q, %q, %q) returns wrong result (-want +got):\n%s", + tc.testName, fakeProject, fakeRepository, tc.benchmarkRoot, diff) + } + } } // Return addons as a string slice for the given cluster.