diff --git a/.secrets.baseline b/.secrets.baseline index 014d253e1..75a26224f 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": { + ".git/config": [ + { + "type": "Base64 High Entropy String", + "filename": ".git/config", + "hashed_secret": "a16bf940eb9599e3a77ae599906a4e71e4e52243", + "is_verified": false, + "line_number": 23 + } + ], "apis/clusterresources/v1beta1/cassandrauser_types.go": [ { "type": "Secret Keyword", @@ -185,21 +190,35 @@ "filename": "apis/clusters/v1beta1/cassandra_types.go", "hashed_secret": "331cc743251c3b9504229de4d139c539da121a33", "is_verified": false, - "line_number": 261 + "line_number": 263 }, { "type": "Secret Keyword", "filename": "apis/clusters/v1beta1/cassandra_types.go", "hashed_secret": "0ad8d7005e084d4f028a4277b73c6fab24269c17", "is_verified": false, - "line_number": 347 + "line_number": 349 }, { "type": "Secret Keyword", "filename": "apis/clusters/v1beta1/cassandra_types.go", "hashed_secret": "e0a46b27231f798fe22dc4d5d82b5feeb5dcf085", "is_verified": false, - "line_number": 412 + "line_number": 414 + }, + { + "type": "Secret Keyword", + "filename": "apis/clusters/v1beta1/cassandra_types.go", + "hashed_secret": "5ffe533b830f08a0326348a9160afafc8ada44db", + "is_verified": false, + "line_number": 628 + }, + { + "type": "Secret Keyword", + "filename": "apis/clusters/v1beta1/cassandra_types.go", + "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", + "is_verified": false, + "line_number": 633 } ], "apis/clusters/v1beta1/cassandra_webhook.go": [ @@ -319,21 +338,21 @@ "filename": "apis/clusters/v1beta1/postgresql_types.go", "hashed_secret": "5ffe533b830f08a0326348a9160afafc8ada44db", "is_verified": false, - "line_number": 355 + "line_number": 354 }, { "type": "Secret Keyword", "filename": "apis/clusters/v1beta1/postgresql_types.go", "hashed_secret": "a3d7d4a96d18c8fc5a1cf9c9c01c45b4690b4008", "is_verified": false, - "line_number": 361 + "line_number": 360 }, { "type": "Secret Keyword", "filename": "apis/clusters/v1beta1/postgresql_types.go", "hashed_secret": "a57ce131bd944bdf8ba2f2f93e179dc416ed0315", "is_verified": false, - "line_number": 481 + "line_number": 480 } ], "apis/clusters/v1beta1/redis_types.go": [ @@ -390,7 +409,7 @@ "filename": "apis/clusters/v1beta1/zz_generated.deepcopy.go", "hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c", "is_verified": false, - "line_number": 1322 + "line_number": 601 } ], "apis/kafkamanagement/v1beta1/kafkauser_types.go": [ @@ -1146,5 +1165,5 @@ } ] }, - "generated_at": "2024-02-19T13:01:03Z" + "generated_at": "2024-02-21T10:45:53Z" } diff --git a/apis/clusters/v1beta1/cassandra_types.go b/apis/clusters/v1beta1/cassandra_types.go index 5808ee3f9..e1dc06edd 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" @@ -64,8 +65,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"` } @@ -611,3 +613,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 50c6ab8cf..96a8255eb 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 7b16982a6..26f8ad365 100644 --- a/config/crd/bases/clusters.instaclustr.com_cassandras.yaml +++ b/config/crd/bases/clusters.instaclustr.com_cassandras.yaml @@ -453,6 +453,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/config/samples/clusters_v1beta1_cassandra.yaml b/config/samples/clusters_v1beta1_cassandra.yaml index 1f3797102..2acf7ca63 100644 --- a/config/samples/clusters_v1beta1_cassandra.yaml +++ b/config/samples/clusters_v1beta1_cassandra.yaml @@ -3,7 +3,7 @@ kind: Cassandra metadata: name: cassandra-cluster spec: - name: "example-cassandra" #(immutable) + name: "mykyta-cassandra" #(immutable) version: "4.0.10" #(immutable) privateNetwork: false #(immutable) dataCentres: 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")