From e15e84b789557b4824262627b807262a17f4a16f Mon Sep 17 00:00:00 2001 From: Bohdan Siryk Date: Mon, 13 Nov 2023 14:13:20 +0200 Subject: [PATCH] generic solution for handling changes in user resources --- .../v1beta1/cassandrauser_types.go | 8 + apis/clusters/v1beta1/cassandra_types.go | 12 +- apis/clusters/v1beta1/kafka_types.go | 2 +- apis/clusters/v1beta1/opensearch_types.go | 2 +- apis/clusters/v1beta1/postgresql_types.go | 2 +- apis/clusters/v1beta1/redis_types.go | 2 +- apis/clusters/v1beta1/structs.go | 23 ++- apis/clusters/v1beta1/structs_test.go | 69 +++++-- .../clusters/v1beta1/zz_generated.deepcopy.go | 104 +++++----- controllers/clusters/cassandra_controller.go | 186 +++--------------- controllers/clusters/helpers.go | 90 +++++++++ controllers/clusters/kafka_controller.go | 8 +- controllers/clusters/opensearch_controller.go | 8 +- controllers/clusters/postgresql_controller.go | 8 +- controllers/clusters/redis_controller.go | 8 +- .../tests/cassandra_plus_users_test.go | 8 +- controllers/tests/helpers.go | 2 +- .../tests/opensearch_plus_users_test.go | 12 +- 18 files changed, 283 insertions(+), 271 deletions(-) diff --git a/apis/clusterresources/v1beta1/cassandrauser_types.go b/apis/clusterresources/v1beta1/cassandrauser_types.go index f68c18434..b3906302e 100644 --- a/apis/clusterresources/v1beta1/cassandrauser_types.go +++ b/apis/clusterresources/v1beta1/cassandrauser_types.go @@ -76,3 +76,11 @@ func (r *CassandraUser) ToInstAPI(username, password string) *models.InstaUser { func (r *CassandraUser) GetDeletionFinalizer() string { return models.DeletionFinalizer + "_" + r.Namespace + "_" + r.Name } + +func (r *CassandraUser) GetClusterEvents() map[string]string { + return r.Status.ClustersEvents +} + +func (r *CassandraUser) SetClusterEvents(events map[string]string) { + r.Status.ClustersEvents = events +} diff --git a/apis/clusters/v1beta1/cassandra_types.go b/apis/clusters/v1beta1/cassandra_types.go index f7dcd8dfc..3c23ba6e0 100644 --- a/apis/clusters/v1beta1/cassandra_types.go +++ b/apis/clusters/v1beta1/cassandra_types.go @@ -62,7 +62,7 @@ type CassandraSpec struct { PasswordAndUserAuth bool `json:"passwordAndUserAuth,omitempty"` Spark []*Spark `json:"spark,omitempty"` BundledUseOnly bool `json:"bundledUseOnly,omitempty"` - UserRefs UserReferences `json:"userRefs,omitempty"` + UserRefs References `json:"userRefs,omitempty"` //+kubebuilder:validate:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` } @@ -70,7 +70,7 @@ type CassandraSpec struct { // CassandraStatus defines the observed state of Cassandra type CassandraStatus struct { ClusterStatus `json:",inline"` - AvailableUsers UserReferences `json:"availableUsers,omitempty"` + AvailableUsers References `json:"availableUsers,omitempty"` } type CassandraDataCentre struct { @@ -495,6 +495,14 @@ func (c *CassandraSpec) validateResizeSettings(nodeNumber int) error { return nil } +func (c *Cassandra) GetAvailableUsers() References { + return c.Status.AvailableUsers +} + +func (c *Cassandra) SetAvailableUsers(users References) { + c.Status.AvailableUsers = users +} + func init() { SchemeBuilder.Register(&Cassandra{}, &CassandraList{}) } diff --git a/apis/clusters/v1beta1/kafka_types.go b/apis/clusters/v1beta1/kafka_types.go index 2c6900c26..52acd5d51 100644 --- a/apis/clusters/v1beta1/kafka_types.go +++ b/apis/clusters/v1beta1/kafka_types.go @@ -88,7 +88,7 @@ type KafkaSpec struct { KarapaceRestProxy []*KarapaceRestProxy `json:"karapaceRestProxy,omitempty"` KarapaceSchemaRegistry []*KarapaceSchemaRegistry `json:"karapaceSchemaRegistry,omitempty"` BundledUseOnly bool `json:"bundledUseOnly,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + UserRefs []*Reference `json:"userRefs,omitempty"` Kraft []*Kraft `json:"kraft,omitempty"` } diff --git a/apis/clusters/v1beta1/opensearch_types.go b/apis/clusters/v1beta1/opensearch_types.go index 959e60f9e..a0ca59d28 100644 --- a/apis/clusters/v1beta1/opensearch_types.go +++ b/apis/clusters/v1beta1/opensearch_types.go @@ -53,7 +53,7 @@ type OpenSearchSpec struct { IndexManagementPlugin bool `json:"indexManagementPlugin,omitempty"` AlertingPlugin bool `json:"alertingPlugin,omitempty"` BundledUseOnly bool `json:"bundledUseOnly,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + UserRefs []*Reference `json:"userRefs,omitempty"` //+kubuilder:validation:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` //+kubuilder:validation:MaxItems:=1 diff --git a/apis/clusters/v1beta1/postgresql_types.go b/apis/clusters/v1beta1/postgresql_types.go index 91924f5d7..bde017933 100644 --- a/apis/clusters/v1beta1/postgresql_types.go +++ b/apis/clusters/v1beta1/postgresql_types.go @@ -78,7 +78,7 @@ type PgSpec struct { DataCentres []*PgDataCentre `json:"dataCentres,omitempty"` ClusterConfigurations map[string]string `json:"clusterConfigurations,omitempty"` SynchronousModeStrict bool `json:"synchronousModeStrict,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + UserRefs []*Reference `json:"userRefs,omitempty"` //+kubebuilder:validate:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` } diff --git a/apis/clusters/v1beta1/redis_types.go b/apis/clusters/v1beta1/redis_types.go index bd384d6b2..0bc202f00 100644 --- a/apis/clusters/v1beta1/redis_types.go +++ b/apis/clusters/v1beta1/redis_types.go @@ -69,7 +69,7 @@ type RedisSpec struct { //+kubebuilder:validation:MaxItems:=2 DataCentres []*RedisDataCentre `json:"dataCentres,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + UserRefs []*Reference `json:"userRefs,omitempty"` //+kubebuilder:validation:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` } diff --git a/apis/clusters/v1beta1/structs.go b/apis/clusters/v1beta1/structs.go index 19b331311..f8444df55 100644 --- a/apis/clusters/v1beta1/structs.go +++ b/apis/clusters/v1beta1/structs.go @@ -20,6 +20,8 @@ import ( "encoding/json" "net" + "k8s.io/apimachinery/pkg/types" + clusterresource "github.com/instaclustr/operator/apis/clusterresources/v1beta1" "github.com/instaclustr/operator/pkg/models" ) @@ -711,17 +713,24 @@ func (cs *ClusterStatus) PrivateLinkStatusesEqual(iStatus *ClusterStatus) bool { return true } -type UserReference struct { - Namespace string `json:"namespace"` +type Reference struct { Name string `json:"name"` + Namespace string `json:"namespace"` +} + +func (r *Reference) AsNamespacedName() types.NamespacedName { + return types.NamespacedName{ + Name: r.Name, + Namespace: r.Namespace, + } } -type UserReferences []*UserReference +type References []*Reference -// Diff returns difference between two UserReferences. -// added stores elements which are presented in new UserReferences, but aren't presented in old. -// deleted stores elements which aren't presented in new UserReferences, but are presented in old. -func (old UserReferences) Diff(new UserReferences) (added, deleted UserReferences) { +// Diff returns difference between two References. +// Added stores elements which are presented in new References, but aren't presented in old. +// Deleted stores elements which aren't presented in new References, but are presented in old. +func (old References) Diff(new References) (added, deleted References) { // filtering deleted references for _, oldRef := range old { var exists bool diff --git a/apis/clusters/v1beta1/structs_test.go b/apis/clusters/v1beta1/structs_test.go index e0a932043..3f1fd4ef6 100644 --- a/apis/clusters/v1beta1/structs_test.go +++ b/apis/clusters/v1beta1/structs_test.go @@ -13,20 +13,20 @@ func TestUserReferencesDiff(t *testing.T) { cases := []struct { name string - old v1beta1.UserReferences - new v1beta1.UserReferences - expectedAdded v1beta1.UserReferences - expectedDeleted v1beta1.UserReferences + old v1beta1.References + new v1beta1.References + expectedAdded v1beta1.References + expectedDeleted v1beta1.References }{ { name: "nothing changed", - old: v1beta1.UserReferences{ + old: v1beta1.References{ { Name: "name1", Namespace: "namespace1", }, }, - new: v1beta1.UserReferences{ + new: v1beta1.References{ { Name: "name1", Namespace: "namespace1", @@ -35,13 +35,13 @@ func TestUserReferencesDiff(t *testing.T) { }, { name: "added new reference", - old: v1beta1.UserReferences{ + old: v1beta1.References{ { Name: "name1", Namespace: "namespace1", }, }, - new: v1beta1.UserReferences{ + new: v1beta1.References{ { Name: "name1", Namespace: "namespace1", @@ -51,7 +51,7 @@ func TestUserReferencesDiff(t *testing.T) { Namespace: "namespace2", }, }, - expectedAdded: v1beta1.UserReferences{ + expectedAdded: v1beta1.References{ { Name: "name2", Namespace: "namespace2", @@ -60,7 +60,7 @@ func TestUserReferencesDiff(t *testing.T) { }, { name: "deleted old reference", - old: v1beta1.UserReferences{ + old: v1beta1.References{ { Name: "name1", Namespace: "namespace1", @@ -70,13 +70,13 @@ func TestUserReferencesDiff(t *testing.T) { Namespace: "namespace2", }, }, - new: v1beta1.UserReferences{ + new: v1beta1.References{ { Name: "name1", Namespace: "namespace1", }, }, - expectedDeleted: v1beta1.UserReferences{ + expectedDeleted: v1beta1.References{ { Name: "name2", Namespace: "namespace2", @@ -90,7 +90,7 @@ func TestUserReferencesDiff(t *testing.T) { }, { name: "deleting the first out of 3", - old: v1beta1.UserReferences{ + old: v1beta1.References{ { Name: "name1", Namespace: "namespace1", @@ -104,7 +104,7 @@ func TestUserReferencesDiff(t *testing.T) { Namespace: "namespace3", }, }, - new: v1beta1.UserReferences{ + new: v1beta1.References{ { Name: "name2", Namespace: "namespace2", @@ -114,7 +114,7 @@ func TestUserReferencesDiff(t *testing.T) { Namespace: "namespace3", }, }, - expectedDeleted: v1beta1.UserReferences{ + expectedDeleted: v1beta1.References{ { Name: "name1", Namespace: "namespace1", @@ -123,7 +123,7 @@ func TestUserReferencesDiff(t *testing.T) { }, { name: "deleting the first and adding a new one", - old: v1beta1.UserReferences{ + old: v1beta1.References{ { Name: "name1", Namespace: "namespace1", @@ -137,7 +137,7 @@ func TestUserReferencesDiff(t *testing.T) { Namespace: "namespace3", }, }, - new: v1beta1.UserReferences{ + new: v1beta1.References{ { Name: "name2", Namespace: "namespace2", @@ -151,13 +151,44 @@ func TestUserReferencesDiff(t *testing.T) { Namespace: "namespace4", }, }, - expectedDeleted: v1beta1.UserReferences{ + expectedDeleted: v1beta1.References{ { Name: "name1", Namespace: "namespace1", }, }, - expectedAdded: v1beta1.UserReferences{ + expectedAdded: v1beta1.References{ + { + Name: "name4", + Namespace: "namespace4", + }, + }, + }, + { + name: "deleting the whole references", + old: v1beta1.References{ + { + Name: "name2", + Namespace: "namespace2", + }, + { + Name: "name3", + Namespace: "namespace3", + }, + { + Name: "name4", + Namespace: "namespace4", + }, + }, + expectedDeleted: v1beta1.References{ + { + Name: "name2", + Namespace: "namespace2", + }, + { + Name: "name3", + Namespace: "namespace3", + }, { Name: "name4", Namespace: "namespace4", diff --git a/apis/clusters/v1beta1/zz_generated.deepcopy.go b/apis/clusters/v1beta1/zz_generated.deepcopy.go index 3ac81cafb..50328d2aa 100644 --- a/apis/clusters/v1beta1/zz_generated.deepcopy.go +++ b/apis/clusters/v1beta1/zz_generated.deepcopy.go @@ -482,11 +482,11 @@ func (in *CassandraSpec) DeepCopyInto(out *CassandraSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make(UserReferences, len(*in)) + *out = make(References, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(Reference) **out = **in } } @@ -520,11 +520,11 @@ func (in *CassandraStatus) DeepCopyInto(out *CassandraStatus) { in.ClusterStatus.DeepCopyInto(&out.ClusterStatus) if in.AvailableUsers != nil { in, out := &in.AvailableUsers, &out.AvailableUsers - *out = make(UserReferences, len(*in)) + *out = make(References, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(Reference) **out = **in } } @@ -1200,11 +1200,11 @@ func (in *KafkaSpec) DeepCopyInto(out *KafkaSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*Reference, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(Reference) **out = **in } } @@ -1546,11 +1546,11 @@ func (in *OpenSearchSpec) DeepCopyInto(out *OpenSearchSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*Reference, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(Reference) **out = **in } } @@ -1789,11 +1789,11 @@ func (in *PgSpec) DeepCopyInto(out *PgSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*Reference, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(Reference) **out = **in } } @@ -2090,11 +2090,11 @@ func (in *RedisSpec) DeepCopyInto(out *RedisSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*Reference, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(Reference) **out = **in } } @@ -2138,6 +2138,46 @@ func (in *RedisStatus) DeepCopy() *RedisStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Reference) DeepCopyInto(out *Reference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Reference. +func (in *Reference) DeepCopy() *Reference { + if in == nil { + return nil + } + out := new(Reference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in References) DeepCopyInto(out *References) { + { + in := &in + *out = make(References, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Reference) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new References. +func (in References) DeepCopy() References { + if in == nil { + return nil + } + out := new(References) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ReplaceOperation) DeepCopyInto(out *ReplaceOperation) { *out = *in @@ -2432,46 +2472,6 @@ func (in *TwoFactorDelete) DeepCopy() *TwoFactorDelete { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UserReference) DeepCopyInto(out *UserReference) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserReference. -func (in *UserReference) DeepCopy() *UserReference { - if in == nil { - return nil - } - out := new(UserReference) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in UserReferences) DeepCopyInto(out *UserReferences) { - { - in := &in - *out = make(UserReferences, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) - **out = **in - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserReferences. -func (in UserReferences) DeepCopy() UserReferences { - if in == nil { - return nil - } - out := new(UserReferences) - in.DeepCopyInto(out) - return *out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Zookeeper) DeepCopyInto(out *Zookeeper) { *out = *in diff --git a/controllers/clusters/cassandra_controller.go b/controllers/clusters/cassandra_controller.go index 5ec53ec73..63803d33e 100644 --- a/controllers/clusters/cassandra_controller.go +++ b/controllers/clusters/cassandra_controller.go @@ -385,32 +385,17 @@ func (r *CassandraReconciler) handleUpdateCluster( } } - addedRefs, deletedRefs := cassandra.Status.AvailableUsers.Diff(cassandra.Spec.UserRefs) - - for _, ref := range addedRefs { - err = r.handleUsersCreate(ctx, l, cassandra, ref) - if err != nil { - return reconcile.Result{}, err - } - } - - for _, ref := range deletedRefs { - err = r.handleUsersDelete(ctx, l, cassandra, ref) - if err != nil { - return reconcile.Result{}, err - } - } - - if addedRefs != nil || deletedRefs != nil { - cassandra.Status.AvailableUsers = cassandra.Spec.UserRefs - err = r.Status().Patch(ctx, cassandra, patch) - if err != nil { - l.Error(err, "Failed to patch cluster status with created user") - r.EventRecorder.Eventf(cassandra, models.Warning, models.CreationFailed, - "Failed to create a user for the cluster. Reason: %v", err) - - return reconcile.Result{}, err - } + err = handleUsersChanges( + ctx, + r.Client, + cassandra.Status.AvailableUsers, + cassandra.Spec.UserRefs, + cassandra, + r, + cassandra.Status.ID, + ) + if err != nil { + return reconcile.Result{}, err } cassandra.Annotations[models.ResourceStateAnnotation] = models.UpdatedEvent @@ -637,143 +622,11 @@ func (r *CassandraReconciler) handleDeleteCluster( return models.ExitReconcile, nil } -func (r *CassandraReconciler) handleUsersCreate( - ctx context.Context, - l logr.Logger, - c *v1beta1.Cassandra, - uRef *v1beta1.UserReference, -) error { - req := types.NamespacedName{ - Namespace: uRef.Namespace, - Name: uRef.Name, - } - - u := &clusterresourcesv1beta1.CassandraUser{} - err := r.Get(ctx, req, u) - if err != nil { - if k8serrors.IsNotFound(err) { - l.Error(err, "Cannot create a Cassandra user. The resource is not found", "request", req) - r.EventRecorder.Eventf(c, models.Warning, models.NotFound, - "User is not found, create a new one Cassandra User or provide correct userRef."+ - "Current provided reference: %v", uRef) - return err - } - - l.Error(err, "Cannot get Cassandra user", "user", u.Spec) - r.EventRecorder.Eventf(c, models.Warning, models.CreationFailed, - "Cannot get Cassandra user. user reference: %v", uRef) - return err - } - - if _, exist := u.Status.ClustersEvents[c.Status.ID]; exist { - l.Info("User is already existing on the cluster", - "user reference", uRef) - r.EventRecorder.Eventf(c, models.Normal, models.CreationFailed, - "User is already existing on the cluster. User reference: %v", uRef) - - return nil - } - - patch := u.NewPatch() - - if u.Status.ClustersEvents == nil { - u.Status.ClustersEvents = make(map[string]string) - } - - u.Status.ClustersEvents[c.Status.ID] = models.CreatingEvent - - err = r.Status().Patch(ctx, u, patch) - if err != nil { - l.Error(err, "Cannot patch the Cassandra User status with the CreatingEvent", - "cluster name", c.Spec.Name, "cluster ID", c.Status.ID) - r.EventRecorder.Eventf(c, models.Warning, models.CreationFailed, - "Cannot add Cassandra User to the cluster. Reason: %v", err) - return err - } - - l.Info("User has been added to the queue for creation", "username", u.Name) - - return nil -} - -func (r *CassandraReconciler) handleUsersDelete( - ctx context.Context, - l logr.Logger, - c *v1beta1.Cassandra, - uRef *v1beta1.UserReference, -) error { - req := types.NamespacedName{ - Namespace: uRef.Namespace, - Name: uRef.Name, - } - - u := &clusterresourcesv1beta1.CassandraUser{} - err := r.Get(ctx, req, u) - if err != nil { - if k8serrors.IsNotFound(err) { - l.Error(err, "Cannot delete a Cassandra user, the user is not found", "request", req) - r.EventRecorder.Eventf(c, models.Warning, models.NotFound, - "Cannot delete a Cassandra user, the user %v is not found", req) - return nil - } - - l.Error(err, "Cannot get Cassandra user", "user", req) - r.EventRecorder.Eventf(c, models.Warning, models.DeletionFailed, - "Cannot get Cassandra user. user reference: %v", req) - return err - } - - if _, exist := u.Status.ClustersEvents[c.Status.ID]; !exist { - l.Info("User is not existing on the cluster", - "user reference", uRef) - r.EventRecorder.Eventf(c, models.Normal, models.DeletionFailed, - "User is not existing on the cluster. User reference: %v", req) - - return nil - } - - patch := u.NewPatch() - - u.Status.ClustersEvents[c.Status.ID] = models.DeletingEvent - - err = r.Status().Patch(ctx, u, patch) - if err != nil { - l.Error(err, "Cannot patch the Cassandra User status with the DeletingEvent", - "cluster name", c.Spec.Name, "cluster ID", c.Status.ID) - r.EventRecorder.Eventf(c, models.Warning, models.DeletionFailed, - "Cannot patch the Cassandra User status with the DeletingEvent. Reason: %v", err) - return err - } - - patch = c.NewPatch() - - for i, ref := range c.Status.AvailableUsers { - if *ref == *uRef { - c.Status.AvailableUsers = append(c.Status.AvailableUsers[:i], c.Status.AvailableUsers[i+1:]...) - break - } - } - - err = r.Status().Patch(ctx, c, patch) - if err != nil { - l.Error(err, "Failed to patch cluster status with created user", "user ref", uRef) - r.EventRecorder.Eventf(c, models.Warning, models.CreationFailed, - "Failed to create a user for the cluster. Reason: %v", err) - return err - } - - l.Info("User has been added to the queue for deletion", - "User resource", u.Namespace+"/"+u.Name, - "Cassandra resource", c.Namespace+"/"+c.Name) - - return nil -} - func (r *CassandraReconciler) handleUsersDetach( ctx context.Context, l logr.Logger, c *v1beta1.Cassandra, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -1125,7 +978,15 @@ func (r *CassandraReconciler) newUsersCreationJob(c *v1beta1.Cassandra) schedule } for _, ref := range c.Spec.UserRefs { - err = r.handleUsersCreate(ctx, l, c, ref) + err = handleUsersChanges( + ctx, + r.Client, + c.Status.AvailableUsers, + c.Spec.UserRefs, + c, + r, + c.Status.ID, + ) if err != nil { l.Error(err, "Failed to create a user for the cluster", "user ref", ref) r.EventRecorder.Eventf(c, models.Warning, models.CreationFailed, @@ -1247,6 +1108,11 @@ func (r *CassandraReconciler) handleExternalDelete(ctx context.Context, c *v1bet return nil } +// NewUserResource implements userResourceFactory interface +func (r *CassandraReconciler) NewUserResource() userObject { + return &clusterresourcesv1beta1.CassandraUser{} +} + // SetupWithManager sets up the controller with the Manager. func (r *CassandraReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/controllers/clusters/helpers.go b/controllers/clusters/helpers.go index 01353baff..85eab21dd 100644 --- a/controllers/clusters/helpers.go +++ b/controllers/clusters/helpers.go @@ -23,6 +23,7 @@ import ( "sort" "github.com/hashicorp/go-version" + "github.com/pkg/errors" v1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" @@ -216,3 +217,92 @@ func deleteDefaultUserSecret( return client.Delete(ctx, secret) } + +type Object interface { + client.Object + NewPatch() client.Patch +} + +type userObject interface { + Object + GetClusterEvents() map[string]string + SetClusterEvents(events map[string]string) +} + +type clusterObject interface { + Object + GetAvailableUsers() v1beta1.References + SetAvailableUsers(users v1beta1.References) +} + +type userResourceFactory interface { + NewUserResource() userObject +} + +func handleUsersChanges( + ctx context.Context, + c client.Client, + oldUsers, + newUsers v1beta1.References, + cluster clusterObject, + userFactory userResourceFactory, + clusterID string, +) error { + l := log.FromContext(ctx) + + // TODO delete log + l.Info("helpers::handleUsersChanges") + + added, deleted := oldUsers.Diff(newUsers) + + for _, ref := range added { + l.Info("setting creating event on resource", "ref", ref.AsNamespacedName()) + err := setEvent(ctx, c, ref, userFactory, clusterID, models.CreatingEvent) + if err != nil { + return err + } + } + + for _, ref := range deleted { + l.Info("setting deleting event on resource", "ref", ref.AsNamespacedName()) + err := setEvent(ctx, c, ref, userFactory, clusterID, models.DeletingEvent) + if err != nil { + return err + } + } + + if added != nil || deleted != nil { + patch := cluster.NewPatch() + cluster.SetAvailableUsers(newUsers) + err := c.Status().Patch(ctx, cluster, patch) + if err != nil { + return errors.Wrap(err, "handleUsersChanges patch cluster status") + } + } + + return nil +} + +func setEvent(ctx context.Context, c client.Client, ref *v1beta1.Reference, userFactory userResourceFactory, clusterID, event string) error { + user := userFactory.NewUserResource() + err := c.Get(ctx, ref.AsNamespacedName(), user) + if err != nil { + return errors.Wrap(err, "setEvent getting resource") + } + + patch := user.NewPatch() + + events := user.GetClusterEvents() + if events == nil { + events = map[string]string{} + user.SetClusterEvents(events) + } + + events[clusterID] = event + err = c.Status().Patch(ctx, user, patch) + if err != nil { + return errors.Wrap(err, "setEvents patching status") + } + + return nil +} diff --git a/controllers/clusters/kafka_controller.go b/controllers/clusters/kafka_controller.go index fa58db605..d1db4ac44 100644 --- a/controllers/clusters/kafka_controller.go +++ b/controllers/clusters/kafka_controller.go @@ -317,7 +317,7 @@ func (r *KafkaReconciler) handleCreateUser( ctx context.Context, kafka *v1beta1.Kafka, l logr.Logger, - userRef *v1beta1.UserReference, + userRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: userRef.Namespace, @@ -377,7 +377,7 @@ func (r *KafkaReconciler) handleDeleteUser( ctx context.Context, kafka *v1beta1.Kafka, l logr.Logger, - userRef *v1beta1.UserReference, + userRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: userRef.Namespace, @@ -433,7 +433,7 @@ func (r *KafkaReconciler) detachUser( ctx context.Context, kafka *v1beta1.Kafka, l logr.Logger, - userRef *v1beta1.UserReference) error { + userRef *v1beta1.Reference) error { req := types.NamespacedName{ Namespace: userRef.Namespace, Name: userRef.Name, @@ -482,7 +482,7 @@ func (r *KafkaReconciler) detachUser( func (r *KafkaReconciler) handleUserEvent( newObj *v1beta1.Kafka, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.Reference, ) { ctx := context.TODO() l := log.FromContext(ctx) diff --git a/controllers/clusters/opensearch_controller.go b/controllers/clusters/opensearch_controller.go index 8d10ea65c..b19a53b27 100644 --- a/controllers/clusters/opensearch_controller.go +++ b/controllers/clusters/opensearch_controller.go @@ -952,7 +952,7 @@ func (r *OpenSearchReconciler) deleteUser( ctx context.Context, l logr.Logger, c *v1beta1.OpenSearch, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -1005,7 +1005,7 @@ func (r *OpenSearchReconciler) createUser( ctx context.Context, logger logr.Logger, c *v1beta1.OpenSearch, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -1064,7 +1064,7 @@ func (r *OpenSearchReconciler) detachUserResource( ctx context.Context, l logr.Logger, c *v1beta1.OpenSearch, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -1110,7 +1110,7 @@ func (r *OpenSearchReconciler) detachUserResource( func (r *OpenSearchReconciler) handleUserEvent( newObj *v1beta1.OpenSearch, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.Reference, ) { ctx := context.TODO() l := log.FromContext(ctx) diff --git a/controllers/clusters/postgresql_controller.go b/controllers/clusters/postgresql_controller.go index ec7bac8f2..046f3f383 100644 --- a/controllers/clusters/postgresql_controller.go +++ b/controllers/clusters/postgresql_controller.go @@ -498,7 +498,7 @@ func (r *PostgreSQLReconciler) createUser( ctx context.Context, l logr.Logger, c *v1beta1.PostgreSQL, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -577,7 +577,7 @@ func (r *PostgreSQLReconciler) handleUsersDelete( ctx context.Context, l logr.Logger, pg *v1beta1.PostgreSQL, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -641,7 +641,7 @@ func (r *PostgreSQLReconciler) handleUsersDetach( ctx context.Context, l logr.Logger, c *v1beta1.PostgreSQL, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -698,7 +698,7 @@ func (r *PostgreSQLReconciler) handleUsersDetach( func (r *PostgreSQLReconciler) handleUserEvent( newObj *v1beta1.PostgreSQL, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.Reference, ) { ctx := context.TODO() l := log.FromContext(ctx) diff --git a/controllers/clusters/redis_controller.go b/controllers/clusters/redis_controller.go index f246ac3cf..0be37a0f8 100644 --- a/controllers/clusters/redis_controller.go +++ b/controllers/clusters/redis_controller.go @@ -419,7 +419,7 @@ func (r *RedisReconciler) handleCreateUsers( ctx context.Context, redis *v1beta1.Redis, l logr.Logger, - userRefs *v1beta1.UserReference, + userRefs *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: userRefs.Namespace, @@ -673,7 +673,7 @@ func (r *RedisReconciler) detachUserResource( ctx context.Context, l logr.Logger, redis *v1beta1.Redis, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -719,7 +719,7 @@ func (r *RedisReconciler) detachUserResource( func (r *RedisReconciler) handleUserEvent( newObj *v1beta1.Redis, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.Reference, ) { ctx := context.TODO() l := log.FromContext(ctx) @@ -777,7 +777,7 @@ func (r *RedisReconciler) handleUsersDelete( ctx context.Context, l logr.Logger, c *v1beta1.Redis, - uRef *v1beta1.UserReference, + uRef *v1beta1.Reference, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, diff --git a/controllers/tests/cassandra_plus_users_test.go b/controllers/tests/cassandra_plus_users_test.go index 53dc111f6..66eb76a4b 100644 --- a/controllers/tests/cassandra_plus_users_test.go +++ b/controllers/tests/cassandra_plus_users_test.go @@ -142,7 +142,7 @@ var _ = Describe("Basic Cassandra User controller + Basic Cassandra cluster cont When("add the user to a Cassandra UserReference", func() { It("should create the user for the cluster", func() { - newUsers := []*v1beta1.UserReference{{ + newUsers := []*v1beta1.Reference{{ Namespace: userManifest1.Namespace, Name: userManifest1.Name, }} @@ -178,7 +178,7 @@ var _ = Describe("Basic Cassandra User controller + Basic Cassandra cluster cont patch := cassandra1.NewPatch() // removing user - cassandra1.Spec.UserRefs = []*v1beta1.UserReference{} + cassandra1.Spec.UserRefs = []*v1beta1.Reference{} Expect(k8sClient.Patch(ctx, &cassandra1, patch)).Should(Succeed()) done := NewChannelWithTimeout(timeout) @@ -227,7 +227,7 @@ var _ = Describe("Basic Cassandra User controller + Basic Cassandra cluster cont Expect(k8sClient.Create(ctx, &userManifest2)).Should(Succeed()) By("adding the batch of users to the cluster, Cassandra(cluster) controller predicate set them creation state") - newUsers := []*v1beta1.UserReference{ + newUsers := []*v1beta1.Reference{ { Namespace: userManifest1.Namespace, Name: userManifest1.Name, @@ -348,7 +348,7 @@ var _ = Describe("Basic Cassandra User controller + Basic Cassandra cluster cont Expect(k8sClient.Get(ctx, userNamespacedName2, &user2)).Should(Succeed()) By("creating another Cassandra cluster manifest with filled user ref, " + "we make sure the user creation job works properly and show us that the user is available for use") - newUsers := []*v1beta1.UserReference{{ + newUsers := []*v1beta1.Reference{{ Namespace: user2.Namespace, Name: user2.Name, }} diff --git a/controllers/tests/helpers.go b/controllers/tests/helpers.go index bf3046f5f..db69a608f 100644 --- a/controllers/tests/helpers.go +++ b/controllers/tests/helpers.go @@ -17,6 +17,6 @@ func NewChannelWithTimeout(timeout time.Duration) chan struct{} { return done } -func removeUserByIndex(s []*v1beta1.UserReference, index int) []*v1beta1.UserReference { +func removeUserByIndex(s []*v1beta1.Reference, index int) []*v1beta1.Reference { return append(s[:index], s[index+1:]...) } diff --git a/controllers/tests/opensearch_plus_users_test.go b/controllers/tests/opensearch_plus_users_test.go index 517d0f8c2..a6fd11740 100644 --- a/controllers/tests/opensearch_plus_users_test.go +++ b/controllers/tests/opensearch_plus_users_test.go @@ -139,9 +139,9 @@ var _ = Describe("Basic openSearch User controller + Basic openSearch cluster co }) }) - When("add the user to a openSearch UserReference", func() { + When("add the user to a openSearch Reference", func() { It("should create the user for the cluster", func() { - newUsers := []*v1beta1.UserReference{{ + newUsers := []*v1beta1.Reference{{ Namespace: userManifest1.Namespace, Name: userManifest1.Name, }} @@ -168,13 +168,13 @@ var _ = Describe("Basic openSearch User controller + Basic openSearch cluster co }) }) - When("remove the user from the openSearch UserReference", func() { + When("remove the user from the openSearch Reference", func() { It("should delete the user for the cluster", func() { Expect(k8sClient.Get(ctx, openSearchNamespacedName1, &openSearch1)).Should(Succeed()) patch := openSearch1.NewPatch() // removing user - openSearch1.Spec.UserRefs = []*v1beta1.UserReference{} + openSearch1.Spec.UserRefs = []*v1beta1.Reference{} Expect(k8sClient.Patch(ctx, &openSearch1, patch)).Should(Succeed()) By("going to openSearch(cluster) controller predicate and put user entity to deletion state. " + "Finally deletes the user for the corresponded cluster") @@ -220,7 +220,7 @@ var _ = Describe("Basic openSearch User controller + Basic openSearch cluster co Expect(k8sClient.Create(ctx, &userManifest2)).Should(Succeed()) By("adding the batch of users to the cluster, openSearch(cluster) controller predicate set them creation state") - newUsers := []*v1beta1.UserReference{ + newUsers := []*v1beta1.Reference{ { Namespace: userManifest1.Namespace, Name: userManifest1.Name, @@ -342,7 +342,7 @@ var _ = Describe("Basic openSearch User controller + Basic openSearch cluster co Expect(k8sClient.Get(ctx, userNamespacedName2, &user2)).Should(Succeed()) By("creating another openSearch cluster manifest with filled user ref, " + "we make sure the user creation job works properly and show us that the user is available for use") - newUsers := []*v1beta1.UserReference{{ + newUsers := []*v1beta1.Reference{{ Namespace: user2.Namespace, Name: user2.Name, }}