From 65b65f6d5c073859a2b69c9d0ddfe40df4b4a18b Mon Sep 17 00:00:00 2001 From: Bohdan Siryk Date: Mon, 4 Mar 2024 11:19:45 +0200 Subject: [PATCH] issue-711, avoiding cluster creation if cluster with provided name exists was implemented --- .secrets.baseline | 21 ++++++++++++------- controllers/clusters/cassandra_controller.go | 15 +++++++++++-- controllers/clusters/helpers.go | 18 ++++++++++++++++ controllers/clusters/kafka_controller.go | 10 +++++++++ .../clusters/kafkaconnect_controller.go | 12 ++++++++++- controllers/clusters/opensearch_controller.go | 11 +++++++++- controllers/clusters/postgresql_controller.go | 11 +++++++++- controllers/clusters/redis_controller.go | 11 +++++++++- controllers/clusters/zookeeper_controller.go | 10 +++++++++ pkg/instaclustr/client.go | 7 ++++--- pkg/instaclustr/interfaces.go | 2 +- pkg/instaclustr/mock/client.go | 4 ++-- .../go/api_account_cluster_list_v2_service.go | 10 ++++----- 13 files changed, 116 insertions(+), 26 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 1eeeec902..ca6d12f63 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -75,10 +75,6 @@ { "path": "detect_secrets.filters.allowlist.is_line_allowlisted" }, - { - "path": "detect_secrets.filters.common.is_baseline_file", - "filename": ".secrets.baseline" - }, { "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", "min_level": 2 @@ -118,6 +114,15 @@ } ], "results": { + ".idea/workspace.xml": [ + { + "type": "Base64 High Entropy String", + "filename": ".idea/workspace.xml", + "hashed_secret": "ceda554256d1b9fa42791b09d9798634f930ad04", + "is_verified": false, + "line_number": 42 + } + ], "apis/clusterresources/v1beta1/cassandrauser_types.go": [ { "type": "Secret Keyword", @@ -528,14 +533,14 @@ "filename": "controllers/clusters/helpers.go", "hashed_secret": "5ffe533b830f08a0326348a9160afafc8ada44db", "is_verified": false, - "line_number": 122 + "line_number": 119 }, { "type": "Secret Keyword", "filename": "controllers/clusters/helpers.go", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 127 + "line_number": 124 } ], "controllers/clusters/kafkaconnect_controller_test.go": [ @@ -553,7 +558,7 @@ "filename": "controllers/clusters/postgresql_controller.go", "hashed_secret": "5ffe533b830f08a0326348a9160afafc8ada44db", "is_verified": false, - "line_number": 1183 + "line_number": 1192 } ], "controllers/clusters/zookeeper_controller_test.go": [ @@ -1125,5 +1130,5 @@ } ] }, - "generated_at": "2024-03-04T13:33:17Z" + "generated_at": "2024-03-05T16:46:11Z" } diff --git a/controllers/clusters/cassandra_controller.go b/controllers/clusters/cassandra_controller.go index 4eec4d2b5..1fb187fa7 100644 --- a/controllers/clusters/cassandra_controller.go +++ b/controllers/clusters/cassandra_controller.go @@ -217,9 +217,20 @@ func (r *CassandraReconciler) createCluster(ctx context.Context, c *v1beta1.Cass var instModel *models.CassandraCluster var err error - if c.Spec.HasRestore() { + id, err := getClusterIDByName(r.API, models.CassandraAppType, c.Spec.Name) + if err != nil { + return err + } + + if id != "" { + l.Info("Cluster with provided name already exists", "name", c.Spec.Name, "clusterID", id) + return fmt.Errorf("cluster %s already exists, please change name property", c.Spec.Name) + } + + switch { + case c.Spec.HasRestore(): instModel, err = r.createCassandraFromRestore(c, l) - } else { + default: instModel, err = r.createCassandra(c, l) } if err != nil { diff --git a/controllers/clusters/helpers.go b/controllers/clusters/helpers.go index 8b3abefec..f988279e7 100644 --- a/controllers/clusters/helpers.go +++ b/controllers/clusters/helpers.go @@ -33,6 +33,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/instaclustr/operator/pkg/instaclustr" "github.com/instaclustr/operator/pkg/models" "github.com/instaclustr/operator/pkg/utils/dcomparison" ) @@ -238,3 +239,20 @@ func incrementCIDR(cidr string) (string, error) { incrementedIP := strings.Join(ipParts, ".") return fmt.Sprintf("%s/%d", incrementedIP, prefixLength), nil } + +func getClusterIDByName(api instaclustr.API, appType string, name string) (string, error) { + clusters, err := api.ListClustersByName(name) + if err != nil { + return "", fmt.Errorf("failed to list clusters by name, err: %w", err) + } + + if len(clusters) == 0 { + return "", nil + } + + if clusters[0].Application != appType { + return "", fmt.Errorf("the cluster %s already exists, but it has other application type %s", name, clusters[0].Application) + } + + return clusters[0].ID, nil +} diff --git a/controllers/clusters/kafka_controller.go b/controllers/clusters/kafka_controller.go index 037a38108..a41dd64c1 100644 --- a/controllers/clusters/kafka_controller.go +++ b/controllers/clusters/kafka_controller.go @@ -107,6 +107,16 @@ func (r *KafkaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl } func (r *KafkaReconciler) createCluster(ctx context.Context, k *v1beta1.Kafka, l logr.Logger) error { + id, err := getClusterIDByName(r.API, models.KafkaAppType, k.Spec.Name) + if err != nil { + return err + } + + if id != "" { + l.Info("Cluster with provided name already exists", "name", k.Spec.Name, "clusterID", id) + return fmt.Errorf("cluster %s already exists, please change name property", k.Spec.Name) + } + l.Info("Creating cluster", "cluster name", k.Spec.Name, "data centres", k.Spec.DataCentres) diff --git a/controllers/clusters/kafkaconnect_controller.go b/controllers/clusters/kafkaconnect_controller.go index 0deea342e..05b39c292 100644 --- a/controllers/clusters/kafkaconnect_controller.go +++ b/controllers/clusters/kafkaconnect_controller.go @@ -123,7 +123,17 @@ func (r *KafkaConnectReconciler) mergeManagedClusterFromRef(ctx context.Context, } func (r *KafkaConnectReconciler) createCluster(ctx context.Context, kc *v1beta1.KafkaConnect, l logr.Logger) error { - err := r.mergeManagedClusterFromRef(ctx, kc) + id, err := getClusterIDByName(r.API, models.KafkaConnectAppType, kc.Spec.Name) + if err != nil { + return err + } + + if id != "" { + l.Info("Cluster with provided name already exists", "name", kc.Spec.Name, "clusterID", id) + return fmt.Errorf("cluster %s already exists, please change name property", kc.Spec.Name) + } + + err = r.mergeManagedClusterFromRef(ctx, kc) if err != nil { return err } diff --git a/controllers/clusters/opensearch_controller.go b/controllers/clusters/opensearch_controller.go index c7fa5a41e..98e8df7a5 100644 --- a/controllers/clusters/opensearch_controller.go +++ b/controllers/clusters/opensearch_controller.go @@ -182,8 +182,17 @@ func (r *OpenSearchReconciler) createOpenSearch(o *v1beta1.OpenSearch, logger lo } func (r *OpenSearchReconciler) createCluster(ctx context.Context, o *v1beta1.OpenSearch, logger logr.Logger) error { + id, err := getClusterIDByName(r.API, models.OpenSearchAppType, o.Spec.Name) + if err != nil { + return err + } + + if id != "" { + logger.Info("Cluster with provided name already exists", "name", o.Spec.Name, "clusterID", id) + return fmt.Errorf("cluster %s already exists, please change name property", o.Spec.Name) + } + var instaModel *models.OpenSearchCluster - var err error if o.Spec.HasRestore() { instaModel, err = r.createOpenSearchFromRestore(o, logger) diff --git a/controllers/clusters/postgresql_controller.go b/controllers/clusters/postgresql_controller.go index a5087fc60..b422c4111 100644 --- a/controllers/clusters/postgresql_controller.go +++ b/controllers/clusters/postgresql_controller.go @@ -181,8 +181,17 @@ func (r *PostgreSQLReconciler) createPostgreSQL(pg *v1beta1.PostgreSQL, l logr.L } func (r *PostgreSQLReconciler) createCluster(ctx context.Context, pg *v1beta1.PostgreSQL, l logr.Logger) error { + id, err := getClusterIDByName(r.API, models.PgAppType, pg.Spec.Name) + if err != nil { + return err + } + + if id != "" { + l.Info("Cluster with provided name already exists", "name", pg.Spec.Name, "clusterID", id) + return fmt.Errorf("cluster %s already exists, please change name property", pg.Spec.Name) + } + var instaModel *models.PGCluster - var err error if pg.Spec.HasRestore() { instaModel, err = r.createFromRestore(pg, l) diff --git a/controllers/clusters/redis_controller.go b/controllers/clusters/redis_controller.go index 68b451d8c..af75480ad 100644 --- a/controllers/clusters/redis_controller.go +++ b/controllers/clusters/redis_controller.go @@ -181,8 +181,17 @@ func (r *RedisReconciler) createRedis(redis *v1beta1.Redis, l logr.Logger) (*mod } func (r *RedisReconciler) createCluster(ctx context.Context, redis *v1beta1.Redis, l logr.Logger) error { + id, err := getClusterIDByName(r.API, models.RedisAppType, redis.Spec.Name) + if err != nil { + return err + } + + if id != "" { + l.Info("Cluster with provided name already exists", "name", redis.Spec.Name, "clusterID", id) + return fmt.Errorf("cluster %s already exists, please change name property", redis.Spec.Name) + } + var instaModel *models.RedisCluster - var err error if redis.Spec.HasRestore() { instaModel, err = r.createFromRestore(redis, l) diff --git a/controllers/clusters/zookeeper_controller.go b/controllers/clusters/zookeeper_controller.go index 07367be69..f20c31816 100644 --- a/controllers/clusters/zookeeper_controller.go +++ b/controllers/clusters/zookeeper_controller.go @@ -104,6 +104,16 @@ func (r *ZookeeperReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } func (r *ZookeeperReconciler) createCluster(ctx context.Context, zook *v1beta1.Zookeeper, l logr.Logger) error { + id, err := getClusterIDByName(r.API, models.ZookeeperAppType, zook.Spec.Name) + if err != nil { + return err + } + + if id != "" { + l.Info("Cluster with provided name already exists", "name", zook.Spec.Name, "clusterID", id) + return fmt.Errorf("cluster %s already exists, please change name property", zook.Spec.Name) + } + l.Info("Creating zookeeper cluster", "cluster name", zook.Spec.Name, "data centres", zook.Spec.DataCentres) diff --git a/pkg/instaclustr/client.go b/pkg/instaclustr/client.go index 8ad362ee0..3ed266a86 100644 --- a/pkg/instaclustr/client.go +++ b/pkg/instaclustr/client.go @@ -2112,8 +2112,9 @@ func (c *Client) UpdatePostgreSQLDefaultUserPassword(id, password string) error return nil } -func (c *Client) ListClusters() ([]*models.ActiveClusters, error) { - url := c.serverHostname + ClustersEndpoint +func (c *Client) ListClustersByName(name string) ([]*models.ActiveCluster, error) { + url := c.serverHostname + ClustersEndpoint + fmt.Sprintf("?search=name:%s", name) + resp, err := c.DoRequest(url, http.MethodGet, nil) if err != nil { return nil, err @@ -2135,7 +2136,7 @@ func (c *Client) ListClusters() ([]*models.ActiveClusters, error) { return nil, err } - return response, nil + return response[0].Clusters, nil } func (c *Client) CreateEncryptionKey( diff --git a/pkg/instaclustr/interfaces.go b/pkg/instaclustr/interfaces.go index 72b679645..013caebb5 100644 --- a/pkg/instaclustr/interfaces.go +++ b/pkg/instaclustr/interfaces.go @@ -94,7 +94,7 @@ type API interface { ResetPostgreSQLConfiguration(id, name string) error GetCadence(id string) (*models.CadenceCluster, error) UpdatePostgreSQLDefaultUserPassword(id, password string) error - ListClusters() ([]*models.ActiveClusters, error) + ListClustersByName(name string) ([]*models.ActiveCluster, error) CreateEncryptionKey(encryptionKeySpec any) (*clusterresourcesv1beta1.AWSEncryptionKeyStatus, error) GetEncryptionKeyStatus(encryptionKeyID string, encryptionKeyEndpoint string) (*clusterresourcesv1beta1.AWSEncryptionKeyStatus, error) DeleteEncryptionKey(encryptionKeyID string) error diff --git a/pkg/instaclustr/mock/client.go b/pkg/instaclustr/mock/client.go index fb6da34f1..dd9c6b8db 100644 --- a/pkg/instaclustr/mock/client.go +++ b/pkg/instaclustr/mock/client.go @@ -358,8 +358,8 @@ func (c *mockClient) UpdateKafkaConnect(id string, kc models.KafkaConnectAPIUpda panic("UpdateKafkaConnect: is not implemented") } -func (c *mockClient) ListClusters() ([]*models.ActiveClusters, error) { - panic("ListClusters: is not implemented") +func (c *mockClient) ListClustersByName(name string) ([]*models.ActiveCluster, error) { + panic("ListClustersByName: is not implemented") } func (c *mockClient) CreateRedisUser(user *models.RedisUser) (string, error) { diff --git a/pkg/instaclustr/mock/server/go/api_account_cluster_list_v2_service.go b/pkg/instaclustr/mock/server/go/api_account_cluster_list_v2_service.go index c5c28b109..c9185634b 100644 --- a/pkg/instaclustr/mock/server/go/api_account_cluster_list_v2_service.go +++ b/pkg/instaclustr/mock/server/go/api_account_cluster_list_v2_service.go @@ -11,8 +11,6 @@ package openapi import ( "context" - "errors" - "net/http" ) // AccountClusterListV2APIService is a service that implements the logic for the AccountClusterListV2APIServicer @@ -31,8 +29,8 @@ func (s *AccountClusterListV2APIService) ClusterManagementV2DataSourcesClustersV // TODO - update ClusterManagementV2DataSourcesClustersV2Get with the required logic for this service method. // Add api_account_cluster_list_v2_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. - // TODO: Uncomment the next line to return response Response(200, []AccountClustersV2{}) or use other options such as http.Ok ... - // return Response(200, []AccountClustersV2{}), nil - - return Response(http.StatusNotImplemented, nil), errors.New("ClusterManagementV2DataSourcesClustersV2Get method not implemented") + return Response(200, []AccountClustersV2{{ + AccountId: "test-account-id", + Clusters: []ClusterSummaryV2{}, + }}), nil }