Skip to content

Commit 58fcf96

Browse files
committed
Improved workload finalization logic, added workload deployment finalizer.
1 parent 93284e8 commit 58fcf96

2 files changed

Lines changed: 73 additions & 19 deletions

File tree

internal/controller/workload_controller.go

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ func (r *WorkloadReconciler) Reconcile(ctx context.Context, req mcreconcile.Requ
177177
_, err := controllerutil.CreateOrUpdate(ctx, cl.GetClient(), deployment, func() error {
178178
if deployment.CreationTimestamp.IsZero() {
179179
logger.Info("creating deployment", "deployment_name", deployment.Name)
180-
deployment.Finalizers = desiredDeployment.Finalizers
181180
if err := controllerutil.SetControllerReference(&workload, deployment, cl.GetScheme()); err != nil {
182181
return fmt.Errorf("failed to set controller on workload deployment: %w", err)
183182
}
@@ -337,21 +336,9 @@ func (r *WorkloadReconciler) Finalize(ctx context.Context, obj client.Object) (f
337336
if deployment.DeletionTimestamp.IsZero() {
338337
// Deletion will result in another reconcile of the workload, where we
339338
// will remove the finalizers.
340-
if err := cl.GetClient().Delete(ctx, &deployment); err != nil {
341-
if apierrors.IsNotFound(err) {
342-
// List cache was not up to date
343-
continue
344-
}
339+
if err := cl.GetClient().Delete(ctx, &deployment); client.IgnoreNotFound(err) != nil {
345340
return finalizer.Result{}, fmt.Errorf("failed deleting workload deployment: %w", err)
346341
}
347-
} else {
348-
// Remove the finalizer if it's still present
349-
if controllerutil.RemoveFinalizer(&deployment, workloadControllerFinalizer) {
350-
if err := cl.GetClient().Update(ctx, &deployment); err != nil {
351-
return finalizer.Result{}, fmt.Errorf("failed removing finalizer from workload deployment: %w", err)
352-
}
353-
}
354-
355342
}
356343
}
357344

@@ -426,9 +413,6 @@ func (r *WorkloadReconciler) getDeploymentsForWorkload(
426413
Labels: map[string]string{
427414
computev1alpha.WorkloadUIDLabel: string(workload.UID),
428415
},
429-
Finalizers: []string{
430-
workloadControllerFinalizer,
431-
},
432416
},
433417
Spec: computev1alpha.WorkloadDeploymentSpec{
434418
WorkloadRef: computev1alpha.WorkloadReference{

internal/controller/workloaddeployment_controller.go

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@ package controller
44

55
import (
66
"context"
7+
"errors"
78
"fmt"
89
"slices"
910

1011
apierrors "k8s.io/apimachinery/pkg/api/errors"
1112
apimeta "k8s.io/apimachinery/pkg/api/meta"
1213
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1314
"k8s.io/apimachinery/pkg/types"
15+
kerrors "k8s.io/apimachinery/pkg/util/errors"
1416
ctrl "sigs.k8s.io/controller-runtime"
1517
"sigs.k8s.io/controller-runtime/pkg/client"
1618
"sigs.k8s.io/controller-runtime/pkg/cluster"
1719
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
20+
"sigs.k8s.io/controller-runtime/pkg/finalizer"
1821
"sigs.k8s.io/controller-runtime/pkg/handler"
1922
"sigs.k8s.io/controller-runtime/pkg/log"
2023
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -32,7 +35,8 @@ import (
3235

3336
// WorkloadDeploymentReconciler reconciles a WorkloadDeployment object
3437
type WorkloadDeploymentReconciler struct {
35-
mgr mcmanager.Manager
38+
mgr mcmanager.Manager
39+
finalizers finalizer.Finalizers
3640
}
3741

3842
// +kubebuilder:rbac:groups=compute.datumapis.com,resources=workloaddeployments,verbs=get;list;watch;create;update;patch;delete
@@ -57,6 +61,24 @@ func (r *WorkloadDeploymentReconciler) Reconcile(ctx context.Context, req mcreco
5761
return ctrl.Result{}, err
5862
}
5963

64+
finalizationResult, err := r.finalizers.Finalize(ctx, &deployment)
65+
if err != nil {
66+
if v, ok := err.(kerrors.Aggregate); ok && v.Is(errDeploymentHasInstances) {
67+
// Don't produce an error in this case and let the watch on deployments
68+
// result in another reconcile schedule.
69+
logger.Info("deployment still has instances, waiting until removal")
70+
return ctrl.Result{}, nil
71+
} else {
72+
return ctrl.Result{}, fmt.Errorf("failed to finalize: %w", err)
73+
}
74+
}
75+
if finalizationResult.Updated {
76+
if err = cl.GetClient().Update(ctx, &deployment); err != nil {
77+
return ctrl.Result{}, fmt.Errorf("failed to update based on finalization result: %w", err)
78+
}
79+
return ctrl.Result{}, nil
80+
}
81+
6082
if !deployment.DeletionTimestamp.IsZero() {
6183
return ctrl.Result{}, nil
6284
}
@@ -332,10 +354,58 @@ func (r *WorkloadDeploymentReconciler) reconcileNetworks(
332354
return result
333355
}
334356

357+
var errDeploymentHasInstances = errors.New("deployment has instances")
358+
359+
func (r *WorkloadDeploymentReconciler) Finalize(ctx context.Context, obj client.Object) (finalizer.Result, error) {
360+
clusterName, ok := mccontext.ClusterFrom(ctx)
361+
if !ok {
362+
return finalizer.Result{}, fmt.Errorf("cluster name not found in context")
363+
}
364+
365+
cl, err := r.mgr.GetCluster(ctx, clusterName)
366+
if err != nil {
367+
return finalizer.Result{}, err
368+
}
369+
370+
var instanceList computev1alpha.InstanceList
371+
listOpts := []client.ListOption{
372+
client.InNamespace(obj.GetNamespace()),
373+
client.MatchingLabels{
374+
computev1alpha.WorkloadDeploymentUIDLabel: string(obj.GetUID()),
375+
},
376+
}
377+
378+
if err := cl.GetClient().List(ctx, &instanceList, listOpts...); err != nil {
379+
return finalizer.Result{}, fmt.Errorf("failed listing instances: %w", err)
380+
}
381+
382+
if len(instanceList.Items) == 0 {
383+
log.FromContext(ctx).Info("instances have been removed")
384+
return finalizer.Result{}, nil
385+
}
386+
387+
// All instances need to be deleted before the deployment may be deleted
388+
for _, instance := range instanceList.Items {
389+
if instance.DeletionTimestamp.IsZero() {
390+
if err := cl.GetClient().Delete(ctx, &instance); client.IgnoreNotFound(err) != nil {
391+
return finalizer.Result{}, fmt.Errorf("failed deleting instance: %w", err)
392+
}
393+
}
394+
}
395+
396+
// Really don't like using errors for communication here. I think we'd need
397+
// to move away from the finalizer helper to ensure we can wait on child
398+
// resources to be gone before allowing the finalizer to be removed.
399+
return finalizer.Result{}, errDeploymentHasInstances
400+
}
401+
335402
// SetupWithManager sets up the controller with the Manager.
336403
func (r *WorkloadDeploymentReconciler) SetupWithManager(mgr mcmanager.Manager) error {
337404
r.mgr = mgr
338-
// TODO(jreese) finalizers
405+
r.finalizers = finalizer.NewFinalizers()
406+
if err := r.finalizers.Register(workloadControllerFinalizer, r); err != nil {
407+
return fmt.Errorf("failed to register finalizer: %w", err)
408+
}
339409
return mcbuilder.ControllerManagedBy(mgr).
340410
For(&computev1alpha.WorkloadDeployment{}, mcbuilder.WithEngageWithLocalCluster(false)).
341411
Owns(&computev1alpha.Instance{}).

0 commit comments

Comments
 (0)