From db225d9f602f3439d136ba7a57532dc6ac201ecc Mon Sep 17 00:00:00 2001 From: Andrea Mazzotti Date: Mon, 24 Jun 2024 11:53:19 +0200 Subject: [PATCH] Add toggle to automatically delete no longer in sync versions Signed-off-by: Andrea Mazzotti --- .obs/chartfile/crds/templates/crds.yaml | 6 ++ Makefile | 2 + api/v1beta1/managedosversionchannel_types.go | 5 ++ ...al.cattle.io_managedosversionchannels.yaml | 6 ++ .../managedosversionchannel_controller.go | 7 ++ ...managedosversionchannel_controller_test.go | 67 +++++++++++++++++++ 6 files changed, 93 insertions(+) diff --git a/.obs/chartfile/crds/templates/crds.yaml b/.obs/chartfile/crds/templates/crds.yaml index aa2381388..71f8940ad 100644 --- a/.obs/chartfile/crds/templates/crds.yaml +++ b/.obs/chartfile/crds/templates/crds.yaml @@ -2540,6 +2540,12 @@ spec: type: object spec: properties: + deleteNoLongerInSyncVersions: + default: false + description: |- + DeleteNoLongerInSyncVersions automatically deletes + all no-longer-in-sync ManagedOSVersions that were created by this channel. + type: boolean options: x-kubernetes-preserve-unknown-fields: true syncInterval: diff --git a/Makefile b/Makefile index 3b4f8c873..4859207ec 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ endif export ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) CHART?=$(shell find $(ROOT_DIR) -type f -name "elemental-operator-$(CHART_VERSION).tgz" -print) +CHART_CRDS?=$(shell find $(ROOT_DIR) -type f -name "elemental-operator-crds-$(CHART_VERSION).tgz" -print) KUBE_VERSION?="v1.27.10" CLUSTER_NAME?="operator-e2e" COMMITDATE?=$(shell git log -n1 --format="%as") @@ -197,6 +198,7 @@ setup-full-cluster: build-docker-operator build-docker-seedimage-builder chart s # thus losing any registration/inventories/os CRDs already created reload-operator: build-docker-operator chart kind load docker-image --name $(CLUSTER_NAME) ${REGISTRY_HEADER}${REPO}:${CHART_VERSION} + helm upgrade -n cattle-elemental-system elemental-operator-crds $(CHART_CRDS) helm upgrade -n cattle-elemental-system elemental-operator $(CHART) .PHONY: vendor diff --git a/api/v1beta1/managedosversionchannel_types.go b/api/v1beta1/managedosversionchannel_types.go index 37efc05b6..541ce50ca 100644 --- a/api/v1beta1/managedosversionchannel_types.go +++ b/api/v1beta1/managedosversionchannel_types.go @@ -39,6 +39,11 @@ type ManagedOSVersionChannelSpec struct { // +optional // +kubebuilder:default:="1h" SyncInterval string `json:"syncInterval,omitempty"` + // DeleteNoLongerInSyncVersions automatically deletes + // all no-longer-in-sync ManagedOSVersions that were created by this channel. + // +optional + // +kubebuilder:default:=false + DeleteNoLongerInSyncVersions bool `json:"deleteNoLongerInSyncVersions,omitempty"` // +kubebuilder:validation:Schemaless // +kubebuilder:validation:XPreserveUnknownFields // +optional diff --git a/config/crd/bases/elemental.cattle.io_managedosversionchannels.yaml b/config/crd/bases/elemental.cattle.io_managedosversionchannels.yaml index 08b7eba2e..ca054b61c 100644 --- a/config/crd/bases/elemental.cattle.io_managedosversionchannels.yaml +++ b/config/crd/bases/elemental.cattle.io_managedosversionchannels.yaml @@ -37,6 +37,12 @@ spec: type: object spec: properties: + deleteNoLongerInSyncVersions: + default: false + description: |- + DeleteNoLongerInSyncVersions automatically deletes + all no-longer-in-sync ManagedOSVersions that were created by this channel. + type: boolean options: x-kubernetes-preserve-unknown-fields: true syncInterval: diff --git a/controllers/managedosversionchannel_controller.go b/controllers/managedosversionchannel_controller.go index 65d87969f..93a56a479 100644 --- a/controllers/managedosversionchannel_controller.go +++ b/controllers/managedosversionchannel_controller.go @@ -375,6 +375,13 @@ func (r *ManagedOSVersionChannelReconciler) createManagedOSVersions(ctx context. logger.Error(err, "Could not patch ManagedOSVersion as no longer in sync", "name", version.Name) return fmt.Errorf("deprecating ManagedOSVersion '%s': %w", version.Name, err) } + if ch.Spec.DeleteNoLongerInSyncVersions { + logger.Info("Auto-deleting no longer in sync ManagedOSVersion due to channel settings", "name", version.Name) + if err := r.Delete(ctx, version); err != nil { + logger.Error(err, "Could not auto-delete no longer in sync ManagedOSVersion") + return fmt.Errorf("auto-deleting ManagedOSVersion '%s': %w", version.Name, err) + } + } } } diff --git a/controllers/managedosversionchannel_controller_test.go b/controllers/managedosversionchannel_controller_test.go index 84b27885f..1f6b82d75 100644 --- a/controllers/managedosversionchannel_controller_test.go +++ b/controllers/managedosversionchannel_controller_test.go @@ -717,6 +717,73 @@ var _ = Describe("managed os version channel controller integration tests", func Expect(managedOSVersion.Annotations[elementalv1.ElementalManagedOSVersionNoLongerSyncedAnnotation]).To(Equal(elementalv1.ElementalManagedOSVersionNoLongerSyncedValue)) }) + It("should auto-delete a version after it's removed from channel", func() { + ch.Spec.Type = "json" + ch.Spec.DeleteNoLongerInSyncVersions = true + + Expect(cl.Create(ctx, ch)).To(Succeed()) + + // Pod is created + Eventually(func() bool { + err := cl.Get(ctx, client.ObjectKey{ + Name: ch.Name, + Namespace: ch.Namespace, + }, pod) + return err == nil + }, 12*time.Second, 2*time.Second).Should(BeTrue()) + setPodPhase(pod, corev1.PodSucceeded) + + Eventually(func() bool { + err := cl.Get(ctx, client.ObjectKey{ + Name: ch.Name, + Namespace: ch.Namespace, + }, ch) + return err == nil && ch.Status.Conditions[0].Status == metav1.ConditionTrue + }, 12*time.Second, 2*time.Second).Should(BeTrue()) + + Expect(cl.Get(ctx, client.ObjectKey{ + Name: "v0.1.0", + Namespace: ch.Namespace, + }, managedOSVersion)).To(Succeed()) + Expect(managedOSVersion.Spec.Version).To(Equal("v0.1.0")) + + // Pod is deleted + Eventually(func() bool { + err := cl.Get(ctx, client.ObjectKey{ + Name: ch.Name, + Namespace: ch.Namespace, + }, pod) + return err != nil && apierrors.IsNotFound(err) + }, 12*time.Second, 2*time.Second).Should(BeTrue()) + + // Simulate a channel content change + syncerProvider.SetJSON(deprecatingJSON) + + // Updating the channel after the minimum time between syncs causes an automatic update + patchBase := client.MergeFrom(ch.DeepCopy()) + ch.Spec.SyncInterval = "10m" + Expect(cl.Patch(ctx, ch, patchBase)).To(Succeed()) + + // Pod is created + Eventually(func() bool { + err := cl.Get(ctx, client.ObjectKey{ + Name: ch.Name, + Namespace: ch.Namespace, + }, pod) + return err == nil + }, 12*time.Second, 2*time.Second).Should(BeTrue()) + setPodPhase(pod, corev1.PodSucceeded) + + // Check deprecated version was deleted + Eventually(func() bool { + err := cl.Get(ctx, client.ObjectKey{ + Name: "v0.1.0", + Namespace: ch.Namespace, + }, managedOSVersion) + return apierrors.IsNotFound(err) + }, 12*time.Second, 2*time.Second).Should(BeTrue(), "No longer in sync version should have been deleted") + }) + It("should not reconcile again if it errors during pod lifecycle", func() { ch.Spec.Type = "json"