From 1d76809dc52a41d6ed49f75858491f1b17d9c70f Mon Sep 17 00:00:00 2001 From: Lee Yarwood Date: Wed, 16 Aug 2023 16:41:01 +0100 Subject: [PATCH] resource: IgnoreObjectOwnedByVirtOperator Signed-off-by: Lee Yarwood --- internal/common/resource.go | 31 +++++++++++++++++++++++++++++++ internal/common/resource_test.go | 15 +++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/internal/common/resource.go b/internal/common/resource.go index c5ca94857..be19c519f 100644 --- a/internal/common/resource.go +++ b/internal/common/resource.go @@ -15,6 +15,7 @@ import ( rbac "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" + kubevirtCorev1 "kubevirt.io/api/core/v1" instancetypev1alpha2 "kubevirt.io/api/instancetype/v1alpha2" instancetypev1beta1 "kubevirt.io/api/instancetype/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -28,6 +29,7 @@ const ( OperationResultCreated OperationResult = "created" OperationResultUpdated OperationResult = "updated" OperationResultDeleted OperationResult = "deleted" + OperationResultIgnored OperationResult = "ignored" ) type StatusMessage = *string @@ -79,6 +81,9 @@ type ReconcileOptions struct { // on changes that don't increase the .metadata.generation field. // For example, labels and annotations. AlwaysCallUpdateFunc bool + + // Ignore any existing objects associated with the virt-operator + IgnoreObjectOwnedByVirtOperator bool } type ReconcileBuilder interface { @@ -88,6 +93,7 @@ type ReconcileBuilder interface { UpdateFunc(ResourceUpdateFunc) ReconcileBuilder StatusFunc(ResourceStatusFunc) ReconcileBuilder ImmutableSpec(getter ResourceSpecGetter) ReconcileBuilder + IgnoreObjectOwnedByVirtOperator() ReconcileBuilder Options(options ReconcileOptions) ReconcileBuilder @@ -161,6 +167,11 @@ func (r *reconcileBuilder) Options(options ReconcileOptions) ReconcileBuilder { return r } +func (r *reconcileBuilder) IgnoreObjectOwnedByVirtOperator() ReconcileBuilder { + r.options.IgnoreObjectOwnedByVirtOperator = true + return r +} + func (r *reconcileBuilder) Reconcile() (ReconcileResult, error) { if r.addLabels { AddAppLabels(r.request.Instance, r.operandName, r.operandComponent, r.resource) @@ -303,6 +314,14 @@ func (r *reconcileBuilder) createOrUpdateWithImmutableSpec(obj client.Object, f } existing := obj.DeepCopyObject().(client.Object) + + // During an upgrade to v0.19.0 we might encounter virt-operator attempting + // to reconcile the same set of common-instancetype resources. If configured + // ignore these requests to reconcile such objects once owned by virt-operator + if r.options.IgnoreObjectOwnedByVirtOperator && isOwnedByVirtOperator(existing) { + return OperationResultIgnored, existing, nil + } + if err := mutate(f, key, obj); err != nil { return OperationResultNone, existing, err } @@ -337,6 +356,14 @@ func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object) return nil } +func isOwnedByVirtOperator(obj client.Object) bool { + if obj.GetLabels() == nil { + return false + } + managedValue, managedOk := obj.GetLabels()[kubevirtCorev1.ManagedByLabel] + return managedOk && managedValue == kubevirtCorev1.ManagedByLabelOperatorValue +} + func setOwner(request *Request, resource client.Object, isClusterRes bool) error { if isClusterRes { resource.SetOwnerReferences(nil) @@ -391,6 +418,10 @@ func logOperation(result OperationResult, resource client.Object, logger logr.Lo logger.Info(fmt.Sprintf("Deleted %s resource: %s", resource.GetObjectKind().GroupVersionKind().Kind, resource.GetName())) + case OperationResultIgnored: + logger.Info(fmt.Sprintf("Ignored %s resource: %s", + resource.GetObjectKind().GroupVersionKind().Kind, + resource.GetName())) } } diff --git a/internal/common/resource_test.go b/internal/common/resource_test.go index fc26a93b3..e61edf9b9 100644 --- a/internal/common/resource_test.go +++ b/internal/common/resource_test.go @@ -18,6 +18,7 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" + kubevirtCorev1 "kubevirt.io/api/core/v1" ssp "kubevirt.io/ssp-operator/api/v1beta2" ) @@ -217,6 +218,20 @@ var _ = Describe("Resource", func() { Expect(err).ToNot(HaveOccurred()) expectEqualResourceExists(newTestResource(namespace), &request) }) + + It("should ignore object when owned by virt-operator when configured", func() { + resource := newTestResource(namespace) + resource.Labels[kubevirtCorev1.ManagedByLabel] = kubevirtCorev1.ManagedByLabelOperatorValue + Expect(request.Client.Create(request.Context, resource)).To(Succeed()) + + res, err := CreateOrUpdate(&request). + NamespacedResource(newTestResource(namespace)). + IgnoreObjectOwnedByVirtOperator(). + Reconcile() + + Expect(err).ToNot(HaveOccurred()) + Expect(res.OperationResult).To(Equal(OperationResultIgnored)) + }) }) Context("Cleanup", func() {