From bba93a44871830608a9a08a1a3c42ab1ccca1785 Mon Sep 17 00:00:00 2001 From: Bychkov Date: Tue, 8 Aug 2023 19:19:41 +0300 Subject: [PATCH] added Cluster Settings v2 Update --- apis/clusters/v1beta1/structs.go | 19 +++++++++++ apis/clusters/v1beta1/validation.go | 12 +++++-- .../clusters.instaclustr.com_cassandras.yaml | 4 ++- ...lusters.instaclustr.com_kafkaconnects.yaml | 2 ++ .../clusters.instaclustr.com_kafkas.yaml | 2 ++ ...clusters.instaclustr.com_opensearches.yaml | 2 ++ .../clusters.instaclustr.com_zookeepers.yaml | 2 ++ .../samples/clusters_v1beta1_cassandra.yaml | 5 +-- controllers/clusters/cassandra_controller.go | 33 +++++++++++++++++-- pkg/instaclustr/client.go | 26 +++++++++++++++ pkg/instaclustr/config.go | 3 +- pkg/instaclustr/interfaces.go | 1 + pkg/instaclustr/mock/client.go | 4 +++ pkg/models/apiv2.go | 5 +++ pkg/models/errors.go | 1 + 15 files changed, 111 insertions(+), 10 deletions(-) diff --git a/apis/clusters/v1beta1/structs.go b/apis/clusters/v1beta1/structs.go index fb675565e..0af70c6c1 100644 --- a/apis/clusters/v1beta1/structs.go +++ b/apis/clusters/v1beta1/structs.go @@ -84,6 +84,8 @@ type Cluster struct { SLATier string `json:"slaTier,omitempty"` TwoFactorDelete []*TwoFactorDelete `json:"twoFactorDelete,omitempty"` + + Description string `json:"description,omitempty"` } type ClusterStatus struct { @@ -150,6 +152,7 @@ func (c *Cluster) IsEqual(cluster Cluster) bool { c.PCICompliance == cluster.PCICompliance && c.PrivateNetworkCluster == cluster.PrivateNetworkCluster && c.SLATier == cluster.SLATier && + c.Description == cluster.Description && c.IsTwoFactorDeleteEqual(cluster.TwoFactorDelete) } @@ -181,6 +184,22 @@ func (c *Cluster) TwoFactorDeletesToInstAPI() (TFDs []*models.TwoFactorDelete) { return } +func (c *Cluster) ClusterSettingsUpdateToInstAPI() (*models.ClusterSettings, error) { + if len(c.TwoFactorDelete) > 1 { + return nil, models.ErrOnlyOneEntityTwoFactorDelete + } + + iTFD := &models.TwoFactorDelete{} + for _, tfd := range c.TwoFactorDelete { + iTFD = tfd.ToInstAPI() + } + + return &models.ClusterSettings{ + Description: c.Description, + TwoFactorDelete: iTFD, + }, nil +} + func (c *Cluster) TwoFactorDeleteToInstAPIv1() *models.TwoFactorDeleteV1 { if len(c.TwoFactorDelete) == 0 { return nil diff --git a/apis/clusters/v1beta1/validation.go b/apis/clusters/v1beta1/validation.go index 79861042a..bac6dee06 100644 --- a/apis/clusters/v1beta1/validation.go +++ b/apis/clusters/v1beta1/validation.go @@ -142,14 +142,20 @@ func validateAppVersion( } func validateTwoFactorDelete(new, old []*TwoFactorDelete) error { - if len(new) != 0 && len(old) == 0 { + if len(old) == 0 && len(new) == 0 || + len(old) == 0 && len(new) == 1 { return nil } + + if len(new) > 1 { + return models.ErrOnlyOneEntityTwoFactorDelete + } + if len(old) != len(new) { return models.ErrImmutableTwoFactorDelete } - if len(old) != 0 && - *old[0] != *new[0] { + + if *old[0] != *new[0] { return models.ErrImmutableTwoFactorDelete } diff --git a/config/crd/bases/clusters.instaclustr.com_cassandras.yaml b/config/crd/bases/clusters.instaclustr.com_cassandras.yaml index ae828c7e5..62d7b0aed 100644 --- a/config/crd/bases/clusters.instaclustr.com_cassandras.yaml +++ b/config/crd/bases/clusters.instaclustr.com_cassandras.yaml @@ -89,6 +89,8 @@ spec: - replicationFactor type: object type: array + description: + type: string luceneEnabled: type: boolean name: @@ -172,7 +174,7 @@ spec: - email type: object type: array - userRef: + userRefs: items: properties: name: diff --git a/config/crd/bases/clusters.instaclustr.com_kafkaconnects.yaml b/config/crd/bases/clusters.instaclustr.com_kafkaconnects.yaml index 9bb1f3df8..27854e9fd 100644 --- a/config/crd/bases/clusters.instaclustr.com_kafkaconnects.yaml +++ b/config/crd/bases/clusters.instaclustr.com_kafkaconnects.yaml @@ -161,6 +161,8 @@ spec: - replicationFactor type: object type: array + description: + type: string name: description: Name [ 3 .. 32 ] characters. type: string diff --git a/config/crd/bases/clusters.instaclustr.com_kafkas.yaml b/config/crd/bases/clusters.instaclustr.com_kafkas.yaml index 97b39830e..a0b71ca08 100644 --- a/config/crd/bases/clusters.instaclustr.com_kafkas.yaml +++ b/config/crd/bases/clusters.instaclustr.com_kafkas.yaml @@ -118,6 +118,8 @@ spec: - nodesNumber type: object type: array + description: + type: string karapaceRestProxy: items: properties: diff --git a/config/crd/bases/clusters.instaclustr.com_opensearches.yaml b/config/crd/bases/clusters.instaclustr.com_opensearches.yaml index a0c7c6027..431d3bf6e 100644 --- a/config/crd/bases/clusters.instaclustr.com_opensearches.yaml +++ b/config/crd/bases/clusters.instaclustr.com_opensearches.yaml @@ -122,6 +122,8 @@ spec: - nodesNumber type: object type: array + description: + type: string icuPlugin: type: boolean indexManagementPlugin: diff --git a/config/crd/bases/clusters.instaclustr.com_zookeepers.yaml b/config/crd/bases/clusters.instaclustr.com_zookeepers.yaml index afe9b5279..19b850958 100644 --- a/config/crd/bases/clusters.instaclustr.com_zookeepers.yaml +++ b/config/crd/bases/clusters.instaclustr.com_zookeepers.yaml @@ -84,6 +84,8 @@ spec: - region type: object type: array + description: + type: string name: description: Name [ 3 .. 32 ] characters. type: string diff --git a/config/samples/clusters_v1beta1_cassandra.yaml b/config/samples/clusters_v1beta1_cassandra.yaml index ba95a50e9..6d57d7936 100644 --- a/config/samples/clusters_v1beta1_cassandra.yaml +++ b/config/samples/clusters_v1beta1_cassandra.yaml @@ -39,8 +39,9 @@ spec: # - namespace: default # name: cassandrauser-sample2 slaTier: "NON_PRODUCTION" - # twoFactorDelete: - # - email: "rostyslp@netapp.com" +# description: "this is a sample of description" +# twoFactorDelete: +# - email: "rostyslp@netapp.com" #spark: # - version: "2.3.2" # 3.0.1 for 4.0.4 version of Cassandra | 2.3.2 for 3.11.13 version of Cassandra diff --git a/controllers/clusters/cassandra_controller.go b/controllers/clusters/cassandra_controller.go index 0a0c65445..3db7090fc 100644 --- a/controllers/clusters/cassandra_controller.go +++ b/controllers/clusters/cassandra_controller.go @@ -316,7 +316,35 @@ func (r *CassandraReconciler) handleUpdateCluster( return r.handleExternalChanges(cassandra, iCassandra, l) } - if !cassandra.Spec.IsEqual(iCassandra.Spec) { + patch := cassandra.NewPatch() + + if len(cassandra.Spec.TwoFactorDelete) != 0 && len(iCassandra.Spec.TwoFactorDelete) == 0 || + cassandra.Spec.Description != iCassandra.Spec.Description { + settingsToInstAPI, err := cassandra.Spec.ClusterSettingsUpdateToInstAPI() + if err != nil { + l.Error(err, "Cannot convert cluster settings to Instaclustr API", + "cluster ID", cassandra.Status.ID, + "cluster spec", cassandra.Spec) + + r.EventRecorder.Eventf(cassandra, models.Warning, models.UpdateFailed, + "Cannot update cluster settings. Reason: %v", err) + + return models.ReconcileRequeue + } + + err = r.API.UpdateClusterSettings(cassandra.Status.ID, settingsToInstAPI) + if err != nil { + l.Error(err, "Cannot update cluster settings", + "cluster ID", cassandra.Status.ID, "cluster spec", cassandra.Spec) + + r.EventRecorder.Eventf(cassandra, models.Warning, models.UpdateFailed, + "Cannot update cluster settings. Reason: %v", err) + + return models.ReconcileRequeue + } + } + + if !cassandra.Spec.AreDCsEqual(iCassandra.Spec.DataCentres) { err = r.API.UpdateCassandra(cassandra.Status.ID, cassandra.Spec.NewDCsUpdate()) if err != nil { l.Error(err, "Cannot update cluster", @@ -357,7 +385,6 @@ func (r *CassandraReconciler) handleUpdateCluster( } } - patch := cassandra.NewPatch() cassandra.Annotations[models.ResourceStateAnnotation] = models.UpdatedEvent cassandra.Annotations[models.UpdateQueuedAnnotation] = "" err = r.Patch(ctx, cassandra, patch) @@ -938,7 +965,7 @@ func (r *CassandraReconciler) newWatchStatusJob(cassandra *v1beta1.Cassandra) sc } if iCassandra.Status.CurrentClusterOperationStatus == models.NoOperation && - cassandra.Annotations[models.ExternalChangesAnnotation] != models.True && + cassandra.Annotations[models.ResourceStateAnnotation] != models.UpdatingEvent && cassandra.Annotations[models.UpdateQueuedAnnotation] != models.True && !cassandra.Spec.IsEqual(iCassandra.Spec) { l.Info(msgExternalChanges, "instaclustr data", iCassandra.Spec, "k8s resource spec", cassandra.Spec) diff --git a/pkg/instaclustr/client.go b/pkg/instaclustr/client.go index b6dac101e..cccf45589 100644 --- a/pkg/instaclustr/client.go +++ b/pkg/instaclustr/client.go @@ -2215,3 +2215,29 @@ func (c *Client) GetDefaultCredentialsV1(clusterID string) (string, string, erro return creds.Username, creds.InstaclustrUserPassword, nil } + +func (c *Client) UpdateClusterSettings(clusterID string, settings *models.ClusterSettings) error { + url := fmt.Sprintf(ClusterSettingsEndpoint, c.serverHostname, clusterID) + + data, err := json.Marshal(settings) + if err != nil { + return err + } + + resp, err := c.DoRequest(url, http.MethodPut, data) + if err != nil { + return err + } + + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("status code: %d, message: %s", resp.StatusCode, body) + } + + return nil +} diff --git a/pkg/instaclustr/config.go b/pkg/instaclustr/config.go index 85cd56e96..28324af15 100644 --- a/pkg/instaclustr/config.go +++ b/pkg/instaclustr/config.go @@ -20,7 +20,7 @@ import "time" const ( DefaultTimeout = time.Second * 60 - OperatorVersion = "k8s v0.0.5" + OperatorVersion = "k8s v0.1.1" ) // constants for API v2 @@ -51,6 +51,7 @@ const ( NodeReloadEndpoint = "%s/cluster-management/v2/operations/applications/postgresql/nodes/v2/%s/reload" AWSEncryptionKeyEndpoint = "/cluster-management/v2/resources/providers/aws/encryption-keys/v2/" ListAppsVersionsEndpoint = "%s/cluster-management/v2/data-sources/applications/%s/versions/v2/" + ClusterSettingsEndpoint = "%s/cluster-management/v2/operations/clusters/v2/%s/change-settings/v2" ) // constants for API v1 diff --git a/pkg/instaclustr/interfaces.go b/pkg/instaclustr/interfaces.go index 19cb6f40a..3747de569 100644 --- a/pkg/instaclustr/interfaces.go +++ b/pkg/instaclustr/interfaces.go @@ -97,4 +97,5 @@ type API interface { DeleteUser(username, clusterID, app string) error ListAppVersions(app string) ([]*models.AppVersions, error) GetDefaultCredentialsV1(clusterID string) (string, string, error) + UpdateClusterSettings(clusterID string, settings *models.ClusterSettings) error } diff --git a/pkg/instaclustr/mock/client.go b/pkg/instaclustr/mock/client.go index b9df227f1..f3a493ee1 100644 --- a/pkg/instaclustr/mock/client.go +++ b/pkg/instaclustr/mock/client.go @@ -347,3 +347,7 @@ func (c *mockClient) DeleteUser(username, clusterID, app string) error { func (c *mockClient) GetDefaultCredentialsV1(clusterID string) (string, string, error) { panic("GetDefaultCredentialsV1: is not implemented") } + +func (c *mockClient) UpdateClusterSettings(clusterID string, settings *models.ClusterSettings) error { + panic("UpdateClusterSettings: is not implemented") +} diff --git a/pkg/models/apiv2.go b/pkg/models/apiv2.go index 1d73a6fe6..8c37bf496 100644 --- a/pkg/models/apiv2.go +++ b/pkg/models/apiv2.go @@ -119,3 +119,8 @@ type AppVersions struct { type PrivateLink struct { AdvertisedHostname string `json:"advertisedHostname"` } + +type ClusterSettings struct { + Description string `json:"description"` + TwoFactorDelete *TwoFactorDelete `json:"twoFactorDelete"` +} diff --git a/pkg/models/errors.go b/pkg/models/errors.go index c7545e11d..914238675 100644 --- a/pkg/models/errors.go +++ b/pkg/models/errors.go @@ -53,4 +53,5 @@ var ( ErrImmutableNodesNumber = errors.New("nodes number is immutable") ErrMissingSecretKeys = errors.New("the secret is missing the correct keys for the user") ErrUserStillExist = errors.New("the user is still attached to cluster") + ErrOnlyOneEntityTwoFactorDelete = errors.New("currently only one entity of two factor delete can be filled") )