diff --git a/config/samples/clusters_v1beta1_redis.yaml b/config/samples/clusters_v1beta1_redis.yaml index 178b26e96..227dfc287 100644 --- a/config/samples/clusters_v1beta1_redis.yaml +++ b/config/samples/clusters_v1beta1_redis.yaml @@ -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..a5582a6b3 100644 --- a/controllers/clusterresources/redisuser_controller.go +++ b/controllers/clusterresources/redisuser_controller.go @@ -143,11 +143,11 @@ func (r *RedisUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if finalizerNeeded { err = r.Update(ctx, secret) if err != nil { - l.Error(err, "Cannot update Cassandra user secret", + l.Error(err, "Cannot update Redis user secret with deletion finalizer", "secret name", secret.Name, "secret namespace", secret.Namespace) r.EventRecorder.Eventf(user, models.Warning, models.UpdatedEvent, - "Cannot assign Cassandra user to a k8s secret. Reason: %v", err) + "Cannot assign Redis user to a k8s secret. Reason: %v", err) return models.ReconcileRequeue, nil } @@ -156,11 +156,11 @@ func (r *RedisUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( controllerutil.AddFinalizer(user, user.DeletionUserFinalizer(clusterID, username)) 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 with deletion finalizer", "secret name", secret.Name, "secret namespace", secret.Namespace) r.EventRecorder.Eventf(user, models.Warning, models.PatchFailed, - "Resource patch is failed. Reason: %v", err) + "Resource patch with deletion user finalizer is failed. Reason: %v", err) return models.ReconcileRequeue, nil } @@ -174,9 +174,9 @@ func (r *RedisUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( userID := fmt.Sprintf(instaclustr.RedisUserIDFmt, clusterID, user.Namespace) err = r.API.DeleteRedisUser(userID) if err != nil { - l.Error(err, "Cannot delete Redis user", "user", user.Name) + l.Error(err, "Cannot delete Redis user on Instaclustr", "user", user.Name) r.EventRecorder.Eventf(user, models.Warning, models.DeletingEvent, - "Cannot delete user. Reason: %v", err) + "Cannot delete Redis user on Instaclustr. Reason: %v", err) return models.ReconcileRequeue, nil } @@ -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,10 +213,23 @@ func (r *RedisUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( continue } + if event == models.ClusterDeletingEvent { + err = r.detachUserFromDeletedCluster(ctx, clusterID, user, l) + if err != nil { + l.Error(err, "Cannot delete Redis user resource", + "secret name", secret.Name, + "secret namespace", secret.Namespace) + r.EventRecorder.Eventf(user, models.Warning, models.PatchFailed, + "Resource deleting is failed. Reason: %v", err) + + return models.ReconcileRequeue, nil + } + } + userID := fmt.Sprintf(instaclustr.RedisUserIDFmt, clusterID, username) err = r.API.UpdateRedisUser(user.ToInstAPIUpdate(password, userID)) if err != nil { - l.Error(err, "Cannot update redis user", + l.Error(err, "Cannot update redis user password", "secret name", user.Spec.SecretRef.Name, "secret namespace", user.Spec.SecretRef.Namespace, "username", username, @@ -254,6 +267,55 @@ 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 err + } + + 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 err + } + 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 err +} + 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..6e7a10743 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,