From c64ae6c5aaecf5aafe0b484d7351b057c5fd9fb5 Mon Sep 17 00:00:00 2001 From: "mykyta.oleksiienko" Date: Wed, 21 Feb 2024 12:47:08 +0200 Subject: [PATCH] issue-698-Save-default-user-ref-for-cassandra --- apis/clusters/v1beta1/cassandra_types.go | 27 ++++++- .../clusters/v1beta1/zz_generated.deepcopy.go | 5 ++ .../clusters.instaclustr.com_cassandras.yaml | 11 +++ controllers/clusters/cassandra_controller.go | 72 +++++++++++++++++++ 4 files changed, 113 insertions(+), 2 deletions(-) diff --git a/apis/clusters/v1beta1/cassandra_types.go b/apis/clusters/v1beta1/cassandra_types.go index afd326d34..cb94485e9 100644 --- a/apis/clusters/v1beta1/cassandra_types.go +++ b/apis/clusters/v1beta1/cassandra_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + "fmt" "strconv" k8scorev1 "k8s.io/api/core/v1" @@ -65,8 +66,9 @@ type CassandraSpec struct { // CassandraStatus defines the observed state of Cassandra type CassandraStatus struct { - GenericStatus `json:",inline"` - DataCentres []*CassandraDataCentreStatus `json:"dataCentres,omitempty"` + GenericStatus `json:",inline"` + DataCentres []*CassandraDataCentreStatus `json:"dataCentres,omitempty"` + DefaultUserSecretRef *Reference `json:"defaultUserSecretRef,omitempty"` AvailableUsers References `json:"availableUsers,omitempty"` } @@ -612,3 +614,24 @@ func (c *Cassandra) GetHeadlessPorts() []k8scorev1.ServicePort { } return headlessPorts } + +func (c *Cassandra) NewDefaultUserSecret(username, password string) *k8scorev1.Secret { + return &k8scorev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: models.SecretKind, + APIVersion: models.K8sAPIVersionV1, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf(models.DefaultUserSecretNameTemplate, models.DefaultUserSecretPrefix, c.Name), + Namespace: c.Namespace, + Labels: map[string]string{ + models.ControlledByLabel: c.Name, + models.DefaultSecretLabel: "true", + }, + }, + StringData: map[string]string{ + models.Username: username, + models.Password: password, + }, + } +} diff --git a/apis/clusters/v1beta1/zz_generated.deepcopy.go b/apis/clusters/v1beta1/zz_generated.deepcopy.go index 204f11f2d..9db91bf03 100644 --- a/apis/clusters/v1beta1/zz_generated.deepcopy.go +++ b/apis/clusters/v1beta1/zz_generated.deepcopy.go @@ -598,6 +598,11 @@ func (in *CassandraStatus) DeepCopyInto(out *CassandraStatus) { } } } + if in.DefaultUserSecretRef != nil { + in, out := &in.DefaultUserSecretRef, &out.DefaultUserSecretRef + *out = new(apiextensions.ObjectReference) + **out = **in + } if in.AvailableUsers != nil { in, out := &in.AvailableUsers, &out.AvailableUsers *out = make(References, len(*in)) diff --git a/config/crd/bases/clusters.instaclustr.com_cassandras.yaml b/config/crd/bases/clusters.instaclustr.com_cassandras.yaml index 5ef899082..78f78d177 100644 --- a/config/crd/bases/clusters.instaclustr.com_cassandras.yaml +++ b/config/crd/bases/clusters.instaclustr.com_cassandras.yaml @@ -449,6 +449,17 @@ spec: - nodes type: object type: array + defaultUserSecretRef: + description: ObjectReference is namespaced reference to an object + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object id: type: string maintenanceEvents: diff --git a/controllers/clusters/cassandra_controller.go b/controllers/clusters/cassandra_controller.go index 24d06faaf..0266d84fa 100644 --- a/controllers/clusters/cassandra_controller.go +++ b/controllers/clusters/cassandra_controller.go @@ -316,6 +316,25 @@ func (r *CassandraReconciler) handleCreateCluster( } } + err := r.createDefaultSecret(ctx, c, l) + if err != nil { + l.Error(err, "Cannot create default secret for Cassandra", + "cluster name", c.Spec.Name, + "clusterID", c.Status.ID, + ) + r.EventRecorder.Eventf( + c, models.Warning, models.CreationFailed, + "Default user secret creation on the Instaclustr is failed. Reason: %v", + err, + ) + + return reconcile.Result{}, err + } + + l.Info("Cassandra cluster has been created", + "cluster ID", c.Status.ID, + ) + if c.Status.State != models.DeletedStatus { patch := c.NewPatch() c.Annotations[models.ResourceStateAnnotation] = models.CreatedEvent @@ -895,6 +914,59 @@ func (r *CassandraReconciler) newWatchBackupsJob(c *v1beta1.Cassandra) scheduler } } +func (r *CassandraReconciler) createDefaultSecret(ctx context.Context, kc *v1beta1.Cassandra, l logr.Logger) error { + username, password, err := r.API.GetDefaultCredentialsV1(kc.Status.ID) + if err != nil { + l.Error(err, "Cannot get default user creds for Cassandra cluster from the Instaclustr API", + "cluster ID", kc.Status.ID, + ) + r.EventRecorder.Eventf(kc, models.Warning, models.FetchFailed, + "Default user password fetch from the Instaclustr API is failed. Reason: %v", err, + ) + + return err + } + + patch := kc.NewPatch() + secret := kc.NewDefaultUserSecret(username, password) + err = r.Create(ctx, secret) + if err != nil { + l.Error(err, "Cannot create secret with default user credentials", + "cluster ID", kc.Status.ID, + ) + r.EventRecorder.Eventf(kc, models.Warning, models.CreationFailed, + "Creating secret with default user credentials is failed. Reason: %v", err, + ) + + return err + } + + l.Info("Default secret was created", + "secret name", secret.Name, + "secret namespace", secret.Namespace, + ) + + kc.Status.DefaultUserSecretRef = &v1beta1.Reference{ + Name: secret.Name, + Namespace: secret.Namespace, + } + + err = r.Status().Patch(ctx, kc, patch) + if err != nil { + l.Error(err, "Cannot patch Cassandra resource", + "cluster name", kc.Spec.Name, + "status", kc.Status) + + r.EventRecorder.Eventf( + kc, models.Warning, models.PatchFailed, + "Cluster resource patch is failed. Reason: %v", err) + + return err + } + + return nil +} + func (r *CassandraReconciler) newUsersCreationJob(c *v1beta1.Cassandra) scheduler.Job { l := log.Log.WithValues("component", "cassandraUsersCreationJob")