From 2b057d81707d68bc8e590f01458993261780bf6f Mon Sep 17 00:00:00 2001 From: "mykyta.oleksiienko" Date: Tue, 18 Jul 2023 12:15:57 +0300 Subject: [PATCH] issue-469-delete-all-users-from-deleted-cluster --- config/samples/clusters_v1beta1_redis.yaml | 16 +-- .../clusterresources/redisuser_controller.go | 54 +++++++- controllers/clusters/redis_controller.go | 117 ++++++++++++++++++ 3 files changed, 178 insertions(+), 9 deletions(-) diff --git a/config/samples/clusters_v1beta1_redis.yaml b/config/samples/clusters_v1beta1_redis.yaml index 178b26e96..a4319b68b 100644 --- a/config/samples/clusters_v1beta1_redis.yaml +++ b/config/samples/clusters_v1beta1_redis.yaml @@ -9,18 +9,18 @@ metadata: app.kubernetes.io/created-by: operator name: redis-sample spec: - name: "testRedis" + name: "mykyta-testRedis" version: "7.0.11" slaTier: "NON_PRODUCTION" clientEncryption: false passwordAndUserAuth: true userRefs: -# - name: redisuser-sample-1 -# namespace: default -# - name: redisuser-sample-2 -# namespace: default -# - name: redisuser-sample-3 -# namespace: default + - name: redisuser-sample-1 + namespace: default + - name: redisuser-sample-2 + namespace: default + - name: redisuser-sample-3 + namespace: default # twoFactorDelete: # - email: "rostyslp@netapp.com" dataCentres: @@ -30,7 +30,7 @@ spec: # nodeSize: "t3.medium-80-r" nodeSize: "t3.small-20-r" masterNodes: 3 - nodesNumber: 3 + nodesNumber: 0 # - region: "US_WEST_2" # name: "testDC2" # cloudProvider: "AWS_VPC" diff --git a/controllers/clusterresources/redisuser_controller.go b/controllers/clusterresources/redisuser_controller.go index f88e6a59c..14f46ac11 100644 --- a/controllers/clusterresources/redisuser_controller.go +++ b/controllers/clusterresources/redisuser_controller.go @@ -201,7 +201,7 @@ func (r *RedisUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( controllerutil.RemoveFinalizer(user, user.DeletionUserFinalizer(clusterID, user.Namespace)) err = r.Patch(ctx, user, patch) if err != nil { - l.Error(err, "Cannot patch Cassandra user resource", + l.Error(err, "Cannot patch Redis user resource", "secret name", secret.Name, "secret namespace", secret.Namespace) r.EventRecorder.Eventf(user, models.Warning, models.PatchFailed, @@ -213,6 +213,12 @@ func (r *RedisUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( continue } + if event == models.ClusterDeletingEvent { + _ = r.detachUserFromDeletedCluster(ctx, clusterID, user, l) + + continue + } + userID := fmt.Sprintf(instaclustr.RedisUserIDFmt, clusterID, username) err = r.API.UpdateRedisUser(user.ToInstAPIUpdate(password, userID)) if err != nil { @@ -254,6 +260,52 @@ func (r *RedisUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return models.ExitReconcile, nil } +func (r *RedisUserReconciler) detachUserFromDeletedCluster( + ctx context.Context, + clusterID string, + user *v1beta1.RedisUser, + logger logr.Logger, +) error { + patch := user.NewPatch() + delete(user.Status.ClustersEvents, clusterID) + err := r.Status().Patch(ctx, user, patch) + if err != nil { + logger.Error(err, "Cannot detach clusterID from the Redis user resource", + "cluster ID", clusterID, + ) + r.EventRecorder.Eventf( + user, models.Warning, models.PatchFailed, + "Detaching clusterID from the Redis user resource has been failed. Reason: %v", + err, + ) + return nil + } + + patch = user.NewPatch() + controllerutil.RemoveFinalizer(user, getDeletionUserFinalizer(clusterID)) + err = r.Patch(ctx, user, patch) + if err != nil { + logger.Error(err, "Cannot delete finalizer from the Redis user resource", + "cluster ID", clusterID, + ) + r.EventRecorder.Eventf( + user, models.Warning, models.PatchFailed, + "Deleting finalizer from the Redis user resource has been failed. Reason: %v", + err, + ) + return nil + } + logger.Info("Redis user has been deleted from the cluster", + "cluster ID", clusterID, + ) + r.EventRecorder.Eventf( + user, models.Normal, models.Deleted, + "Redis user resource has been deleted from the cluster (clusterID: %v)", clusterID, + ) + + return nil +} + func (r *RedisUserReconciler) handleDeleteUser( ctx context.Context, l logr.Logger, diff --git a/controllers/clusters/redis_controller.go b/controllers/clusters/redis_controller.go index 22b3b2d09..d928d00ed 100644 --- a/controllers/clusters/redis_controller.go +++ b/controllers/clusters/redis_controller.go @@ -461,6 +461,7 @@ func (r *RedisReconciler) handleDeleteCluster( redis *v1beta1.Redis, logger logr.Logger, ) reconcile.Result { + _, err := r.API.GetRedis(redis.Status.ID) if err != nil && !errors.Is(err, instaclustr.NotFound) { logger.Error(err, "Cannot get Redis cluster status from Instaclustr", @@ -476,6 +477,23 @@ func (r *RedisReconciler) handleDeleteCluster( return models.ReconcileRequeue } + for _, ref := range redis.Spec.UserRefs { + err = r.detachUserResource(ctx, logger, redis, ref) + if err != nil { + logger.Error(err, "Cannot delete Redis user cluster", + "cluster name", redis.Spec.Name, + "cluster status", redis.Status.State, + ) + + r.EventRecorder.Eventf( + redis, models.Warning, models.DeletionFailed, + "Cluster deletion on the Instaclustr is failed. Reason: %v", + err, + ) + return models.ReconcileRequeue + } + } + if !errors.Is(err, instaclustr.NotFound) { logger.Info("Sending cluster deletion to the Instaclustr API", "cluster name", redis.Spec.Name, @@ -596,6 +614,52 @@ func (r *RedisReconciler) handleDeleteCluster( return models.ExitReconcile } +func (r *RedisReconciler) detachUserResource( + ctx context.Context, + l logr.Logger, + redis *v1beta1.Redis, + uRef *v1beta1.UserReference, +) error { + req := types.NamespacedName{ + Namespace: uRef.Namespace, + Name: uRef.Name, + } + + u := &clusterresourcesv1beta1.RedisUser{} + err := r.Get(ctx, req, u) + if err != nil { + if k8serrors.IsNotFound(err) { + l.Error(err, "Redis user is not found", "request", req) + r.EventRecorder.Eventf(redis, models.Warning, models.NotFound, + "User resource is not found, please provide correct userRef."+ + "Current provided reference: %v", uRef) + return err + } + + l.Error(err, "Cannot get Redis user", "user", u.Spec) + r.EventRecorder.Eventf(redis, models.Warning, models.DeletionFailed, + "Cannot get Redis user. user reference: %v", uRef) + return err + } + + if _, exist := u.Status.ClustersEvents[redis.Status.ID]; !exist { + return nil + } + + patch := u.NewPatch() + u.Status.ClustersEvents[redis.Status.ID] = models.ClusterDeletingEvent + err = r.Status().Patch(ctx, u, patch) + if err != nil { + l.Error(err, "Cannot patch the Redis user status with the ClusterDeletingEvent", + "cluster name", redis.Spec.Name, "cluster ID", redis.Status.ID) + r.EventRecorder.Eventf(redis, models.Warning, models.DeletionFailed, + "Cannot patch the Redis user status with the ClusterDeletingEvent. Reason: %v", err) + return err + } + + return nil +} + func (r *RedisReconciler) handleUserEvent( newObj *v1beta1.Redis, oldUsers []*v1beta1.UserReference, @@ -652,6 +716,59 @@ func (r *RedisReconciler) handleUserEvent( } } +//func (r *RedisReconciler) handleUsersDelete( +// ctx context.Context, +// l logr.Logger, +// c *v1beta1.Redis, +// uRef *v1beta1.UserReference, +//) error { +// req := types.NamespacedName{ +// Namespace: uRef.Namespace, +// Name: uRef.Name, +// } +// +// u := &clusterresourcesv1beta1.RedisUser{} +// err := r.Get(ctx, req, u) +// if err != nil { +// if k8serrors.IsNotFound(err) { +// l.Error(err, "Redis user is not found", "request", req) +// r.EventRecorder.Eventf(c, models.Warning, models.NotFound, +// "User is not found, create a new one Redis User or provide correct userRef."+ +// "Current provided reference: %v", uRef) +// return err +// } +// +// l.Error(err, "Cannot get Redis user", "user", u.Spec) +// r.EventRecorder.Eventf(c, models.Warning, models.DeletionFailed, +// "Cannot get Redis user. User reference: %v", uRef) +// 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", uRef) +// +// 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 Redis 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 Redis User status with the DeletingEvent. Reason: %v", err) +// return err +// } +// +// return nil +//} + func (r *RedisReconciler) handleUsersDelete( ctx context.Context, l logr.Logger,