diff --git a/controllers/duros_controller.go b/controllers/duros_controller.go index b785dd8..936f3fc 100644 --- a/controllers/duros_controller.go +++ b/controllers/duros_controller.go @@ -26,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/predicate" "github.com/metal-stack/duros-go" @@ -88,6 +89,33 @@ func (r *DurosReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl projectID := duros.Spec.MetalProjectID storageClasses := duros.Spec.StorageClasses + if duros.ObjectMeta.DeletionTimestamp.IsZero() { + if !containsString(duros.GetFinalizers(), DurosFinalizerName) { + controllerutil.AddFinalizer(duros, DurosFinalizerName) + if err := r.Update(ctx, duros); err != nil { + return requeue, err + } + } + } else { + // object is being deleted + // we don't pass the cancelled context here because then our deletion + // procedure will stop prematurely + deletionCtx := context.Background() + + if containsString(duros.GetFinalizers(), DurosFinalizerName) { + if err := r.cleanupResources(deletionCtx); err != nil { + return requeue, err + } + + controllerutil.RemoveFinalizer(duros, DurosFinalizerName) + if err := r.Update(deletionCtx, duros); err != nil { + return requeue, err + } + } + + return ctrl.Result{}, nil + } + p, err := r.createProjectIfNotExist(ctx, projectID) if err != nil { return requeue, err @@ -207,3 +235,12 @@ func validateDuros(duros *v1.Duros) error { } return nil } + +func containsString(slice []string, s string) bool { + for _, item := range slice { + if item == s { + return true + } + } + return false +} diff --git a/controllers/resources.go b/controllers/resources.go index 9195624..2b92317 100644 --- a/controllers/resources.go +++ b/controllers/resources.go @@ -13,6 +13,7 @@ import ( storagev1 "github.com/metal-stack/duros-controller/api/v1" apps "k8s.io/api/apps/v1" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" policy "k8s.io/api/policy/v1beta1" rbac "k8s.io/api/rbac/v1" @@ -1017,6 +1018,68 @@ type deletionResource struct { Object client.Object } +func (r *DurosReconciler) cleanupResources(ctx context.Context) error { + log := r.Log.WithName("storage-csi") + log.Info("cleanup csi") + + resources := []deletionResource{ + { + Key: types.NamespacedName{Name: lbCSINodeName, Namespace: namespace}, + Object: &appsv1.DaemonSet{}, + }, + { + Key: types.NamespacedName{Name: lbCSIControllerName, Namespace: namespace}, + Object: &appsv1.StatefulSet{}, + }, + { + Key: types.NamespacedName{Name: storageClassCredentialsRef, Namespace: namespace}, + Object: &corev1.Secret{}, + }, + } + + for i := range clusterRoleBindings { + crb := clusterRoleBindings[i] + resources = append(resources, deletionResource{ + Key: types.NamespacedName{Name: crb.Name, Namespace: crb.Namespace}, + Object: &rbac.ClusterRoleBinding{}, + }) + } + + for i := range clusterRoles { + cr := clusterRoles[i] + resources = append(resources, deletionResource{ + Key: types.NamespacedName{Name: cr.Name, Namespace: cr.Namespace}, + Object: &rbac.ClusterRole{}, + }) + } + + for i := range serviceAccounts { + sa := serviceAccounts[i] + resources = append(resources, deletionResource{ + Key: types.NamespacedName{Name: sa.Name, Namespace: sa.Namespace}, + Object: &corev1.ServiceAccount{}, + }) + } + + for i := range psps { + psp := psps[i] + resources = append(resources, deletionResource{ + Key: types.NamespacedName{Name: psp.Name}, + Object: &policy.PodSecurityPolicy{}, + }) + } + + // we don't clean up the storage classes and CSI driver because there can be volumes that still reference it + + for _, resource := range resources { + if err := r.deleteResourceWithWait(ctx, log, resource); err != nil { + return err + } + } + + return nil +} + func (r *DurosReconciler) deleteResourceWithWait(ctx context.Context, log logr.Logger, resource deletionResource) error { err := r.Shoot.Get(ctx, resource.Key, resource.Object) if err != nil && apierrors.IsNotFound(err) {