Skip to content

Commit

Permalink
issue-469, delete all users from deleted cluster
Browse files Browse the repository at this point in the history
  • Loading branch information
OleksiienkoMykyta authored and taaraora committed Jul 21, 2023
1 parent 6689bb8 commit 2d7972e
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 12 deletions.
4 changes: 4 additions & 0 deletions apis/clusterresources/v1beta1/redisuser_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ func (rs *RedisUserSpec) ToInstAPI(password, clusterID, username string) *models
}
}

func (r *RedisUser) GetDeletionFinalizer() string {
return models.DeletionFinalizer + "_" + r.Namespace + "_" + r.Name
}

func (r *RedisUser) DeletionUserFinalizer(clusterID, namespace string) string {
return models.DeletionUserFinalizer + clusterID + "_" + namespace
}
Expand Down
2 changes: 1 addition & 1 deletion config/samples/clusters_v1beta1_redis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
84 changes: 73 additions & 11 deletions controllers/clusterresources/redisuser_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,28 +139,28 @@ func (r *RedisUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
"User has been created for a Redis cluster. Cluster ID: %s, username: %s",
clusterID, username)

finalizerNeeded := controllerutil.AddFinalizer(secret, models.DeletionFinalizer)
finalizerNeeded := controllerutil.AddFinalizer(secret, user.GetDeletionFinalizer())
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 k8s secret to a Redis user. Reason: %v", err)

return models.ReconcileRequeue, nil
}
}

controllerutil.AddFinalizer(user, user.DeletionUserFinalizer(clusterID, username))
controllerutil.AddFinalizer(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 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
}
Expand All @@ -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 from the cluster. Cluster ID: %s.", "user", clusterID, user.Name)
r.EventRecorder.Eventf(user, models.Warning, models.DeletingEvent,
"Cannot delete user. Reason: %v", err)
"Cannot delete Redis user from the cluster. Cluster ID: %s. Reason: %v", clusterID, err)

return models.ReconcileRequeue, nil
}
Expand All @@ -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,
Expand All @@ -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 detach Redis user resource",
"secret name", secret.Name,
"secret namespace", secret.Namespace)
r.EventRecorder.Eventf(user, models.Warning, models.DeletionFailed,
"Resource detach 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,
Expand Down Expand Up @@ -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
}

controllerutil.RemoveFinalizer(user, user.DeletionUserFinalizer(clusterID, user.Namespace))

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,
Expand All @@ -280,7 +342,7 @@ func (r *RedisUserReconciler) handleDeleteUser(
}
}

controllerutil.RemoveFinalizer(s, models.DeletionFinalizer)
controllerutil.RemoveFinalizer(s, u.GetDeletionFinalizer())
err = r.Update(ctx, s)
if err != nil {
l.Error(err, "Cannot remove finalizer from secret", "secret name", s.Name)
Expand Down
64 changes: 64 additions & 0 deletions controllers/clusters/redis_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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 detach Redis user",
"cluster name", redis.Spec.Name,
"cluster status", redis.Status.State,
)

r.EventRecorder.Eventf(
redis, models.Warning, models.DeletionFailed,
"Cluster detaching 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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 2d7972e

Please sign in to comment.