From 521c21b37c9fc4ac41d9c2fe6019eb140cfba8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Tich=C3=A1k?= Date: Fri, 3 Jul 2026 15:18:56 +0200 Subject: [PATCH 1/2] [control-operator] finalizer for Environment CRD prohibiting deletion until tasks are gone --- .../controller/environment_controller.go | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/control-operator/internal/controller/environment_controller.go b/control-operator/internal/controller/environment_controller.go index c66ec38f..88f511a6 100644 --- a/control-operator/internal/controller/environment_controller.go +++ b/control-operator/internal/controller/environment_controller.go @@ -179,6 +179,8 @@ func labels(environment *aliecsv1alpha1.Environment, nodename string) map[string } } +const environmentFinalizer = "aliecs.alice.cern/environment-finalizer" + // +kubebuilder:rbac:groups=aliecs.alice.cern,resources=environments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=aliecs.alice.cern,resources=environments/status,verbs=get;update;patch // +kubebuilder:rbac:groups=aliecs.alice.cern,resources=environments/finalizers,verbs=update @@ -194,6 +196,42 @@ func labels(environment *aliecsv1alpha1.Environment, nodename string) map[string // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.23.1/pkg/reconcile +func (r *EnvironmentReconciler) handleFinalizer(ctx context.Context, environment *aliecsv1alpha1.Environment, log logr.Logger) (ctrl.Result, bool, error) { + if environment.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(environment, environmentFinalizer) { + controllerutil.AddFinalizer(environment, environmentFinalizer) + if err := r.Update(ctx, environment); err != nil { + return ctrl.Result{}, true, err + } + return ctrl.Result{}, true, nil + } + } else { + if controllerutil.ContainsFinalizer(environment, environmentFinalizer) { + tasks := &aliecsv1alpha1.TaskList{} + if err := r.List(ctx, tasks, client.InNamespace(environment.Namespace), client.MatchingLabels{"environment": environment.Name}); err != nil { + return ctrl.Result{}, true, err + } + if len(tasks.Items) > 0 { + for i := range tasks.Items { + if tasks.Items[i].DeletionTimestamp.IsZero() { + if err := r.Delete(ctx, &tasks.Items[i]); err != nil && !k8serrors.IsNotFound(err) { + return ctrl.Result{}, true, err + } + } + } + log.Info("waiting for tasks to be deleted before removing environment", "remaining", len(tasks.Items)) + return ctrl.Result{}, true, nil + } + controllerutil.RemoveFinalizer(environment, environmentFinalizer) + if err := r.Update(ctx, environment); err != nil { + return ctrl.Result{}, true, err + } + } + return ctrl.Result{}, true, nil + } + return ctrl.Result{}, false, nil +} + func (r *EnvironmentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := logf.FromContext(ctx) @@ -202,6 +240,10 @@ func (r *EnvironmentReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, client.IgnoreNotFound(err) } + if res, stop, err := r.handleFinalizer(ctx, environment, log); err != nil || stop { + return res, err + } + if environment.Status.State == "" { for nodename, tasksReferences := range environment.TaskTemplates.Tasks { log.Info("creating tasks for hostname from references", "hostname", nodename, "number of tasks", len(tasksReferences)) From 8adcf7ebdc34d120195b77c736c9b60b0ff25d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Tich=C3=A1k?= Date: Mon, 6 Jul 2026 17:06:01 +0200 Subject: [PATCH 2/2] fixup! [control-operator] finalizer for Environment CRD prohibiting deletion until tasks are gone --- .../controller/environment_controller.go | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/control-operator/internal/controller/environment_controller.go b/control-operator/internal/controller/environment_controller.go index 88f511a6..d1159bcd 100644 --- a/control-operator/internal/controller/environment_controller.go +++ b/control-operator/internal/controller/environment_controller.go @@ -198,38 +198,39 @@ const environmentFinalizer = "aliecs.alice.cern/environment-finalizer" // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.23.1/pkg/reconcile func (r *EnvironmentReconciler) handleFinalizer(ctx context.Context, environment *aliecsv1alpha1.Environment, log logr.Logger) (ctrl.Result, bool, error) { if environment.DeletionTimestamp.IsZero() { - if !controllerutil.ContainsFinalizer(environment, environmentFinalizer) { - controllerutil.AddFinalizer(environment, environmentFinalizer) - if err := r.Update(ctx, environment); err != nil { - return ctrl.Result{}, true, err - } - return ctrl.Result{}, true, nil - } - } else { if controllerutil.ContainsFinalizer(environment, environmentFinalizer) { - tasks := &aliecsv1alpha1.TaskList{} - if err := r.List(ctx, tasks, client.InNamespace(environment.Namespace), client.MatchingLabels{"environment": environment.Name}); err != nil { - return ctrl.Result{}, true, err - } - if len(tasks.Items) > 0 { - for i := range tasks.Items { - if tasks.Items[i].DeletionTimestamp.IsZero() { - if err := r.Delete(ctx, &tasks.Items[i]); err != nil && !k8serrors.IsNotFound(err) { - return ctrl.Result{}, true, err - } - } - } - log.Info("waiting for tasks to be deleted before removing environment", "remaining", len(tasks.Items)) - return ctrl.Result{}, true, nil + // finalizer already in place, nothing to do: continue reconciliation + return ctrl.Result{}, false, nil + } + controllerutil.AddFinalizer(environment, environmentFinalizer) + return ctrl.Result{}, true, r.Update(ctx, environment) + } + + // environment is being deleted + if !controllerutil.ContainsFinalizer(environment, environmentFinalizer) { + return ctrl.Result{}, true, nil + } + + tasks := &aliecsv1alpha1.TaskList{} + if err := r.List(ctx, tasks, client.InNamespace(environment.Namespace), client.MatchingLabels{"environment": environment.Name}); err != nil { + return ctrl.Result{}, true, err + } + + if len(tasks.Items) > 0 { + for i := range tasks.Items { + if !tasks.Items[i].DeletionTimestamp.IsZero() { + continue } - controllerutil.RemoveFinalizer(environment, environmentFinalizer) - if err := r.Update(ctx, environment); err != nil { + if err := r.Delete(ctx, &tasks.Items[i]); err != nil && !k8serrors.IsNotFound(err) { return ctrl.Result{}, true, err } } + log.Info("waiting for tasks to be deleted before removing environment", "remaining", len(tasks.Items)) return ctrl.Result{}, true, nil } - return ctrl.Result{}, false, nil + + controllerutil.RemoveFinalizer(environment, environmentFinalizer) + return ctrl.Result{}, true, r.Update(ctx, environment) } func (r *EnvironmentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {