From 9f70e10c53037f215318a9b6f0f570daace116a3 Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Tue, 3 Feb 2026 15:01:11 +0100 Subject: [PATCH] Set owner-kind label on ClusterExtensionRevision resources Add the owner-kind label to ClusterExtensionRevision resources to ensure they are properly labeled with both owner-kind and owner-name. Previously, only the owner-name label was set on the ClusterExtensionRevision resource itself. The owner-kind label was only applied to objects within the revision, but not to the revision resource. This change updates the buildClusterExtensionRevision function to set both labels.OwnerKindKey (ClusterExtension) and labels.OwnerNameKey on the ClusterExtensionRevision metadata. Also fixed variable name collision where local variable 'labels' shadowed the imported 'labels' package by renaming it to 'mergedLabels'. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Per Goncalves da Silva --- .../operator-controller/applier/boxcutter.go | 21 +++++++-------- .../applier/boxcutter_test.go | 4 ++- test/e2e/features/install.feature | 26 +++++++++++++++++++ test/e2e/steps/steps.go | 19 ++++++++++++++ 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/internal/operator-controller/applier/boxcutter.go b/internal/operator-controller/applier/boxcutter.go index 794aad20e..8c9d547ef 100644 --- a/internal/operator-controller/applier/boxcutter.go +++ b/internal/operator-controller/applier/boxcutter.go @@ -67,12 +67,7 @@ func (r *SimpleRevisionGenerator) GenerateRevisionFromHelmRelease( if err := yaml.Unmarshal([]byte(doc), &obj); err != nil { return nil, err } - - existingLabels := obj.GetLabels() - labels := make(map[string]string, len(existingLabels)+len(objectLabels)) - maps.Copy(labels, existingLabels) - maps.Copy(labels, objectLabels) - obj.SetLabels(labels) + obj.SetLabels(mergeLabelMaps(obj.GetLabels(), objectLabels)) // Memory optimization: strip large annotations // Note: ApplyStripTransform never returns an error in practice @@ -131,11 +126,7 @@ func (r *SimpleRevisionGenerator) GenerateRevision( // objectLabels objs := make([]ocv1.ClusterExtensionRevisionObject, 0, len(plain)) for _, obj := range plain { - existingLabels := obj.GetLabels() - labels := make(map[string]string, len(existingLabels)+len(objectLabels)) - maps.Copy(labels, existingLabels) - maps.Copy(labels, objectLabels) - obj.SetLabels(labels) + obj.SetLabels(mergeLabelMaps(obj.GetLabels(), objectLabels)) gvk, err := apiutil.GVKForObject(obj, r.Scheme) if err != nil { @@ -219,6 +210,7 @@ func (r *SimpleRevisionGenerator) buildClusterExtensionRevision( ObjectMeta: metav1.ObjectMeta{ Annotations: annotations, Labels: map[string]string{ + labels.OwnerKindKey: ocv1.ClusterExtensionKind, labels.OwnerNameKey: ext.Name, }, }, @@ -666,3 +658,10 @@ func revisionManagementPerms(rev *ocv1.ClusterExtensionRevision) func(user.Info) } } } + +func mergeLabelMaps(m1, m2 map[string]string) map[string]string { + mergedLabels := make(map[string]string, len(m1)+len(m2)) + maps.Copy(mergedLabels, m1) + maps.Copy(mergedLabels, m2) + return mergedLabels +} diff --git a/internal/operator-controller/applier/boxcutter_test.go b/internal/operator-controller/applier/boxcutter_test.go index ad952e29a..f7c1298ce 100644 --- a/internal/operator-controller/applier/boxcutter_test.go +++ b/internal/operator-controller/applier/boxcutter_test.go @@ -109,6 +109,7 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T) "olm.operatorframework.io/service-account-namespace": "test-namespace", }, Labels: map[string]string{ + labels.OwnerKindKey: ocv1.ClusterExtensionKind, labels.OwnerNameKey: "test-123", }, }, @@ -207,8 +208,9 @@ func Test_SimpleRevisionGenerator_GenerateRevision(t *testing.T) { rev, err := b.GenerateRevision(t.Context(), dummyBundle, ext, map[string]string{}, map[string]string{}) require.NoError(t, err) - t.Log("by checking the olm.operatorframework.io/owner-name label is set to the name of the ClusterExtension") + t.Log("by checking the olm.operatorframework.io/owner-name and owner-kind labels are set") require.Equal(t, map[string]string{ + labels.OwnerKindKey: ocv1.ClusterExtensionKind, labels.OwnerNameKey: "test-extension", }, rev.Labels) t.Log("by checking the revision number is 0") diff --git a/test/e2e/features/install.feature b/test/e2e/features/install.feature index c16af88ef..23f75c546 100644 --- a/test/e2e/features/install.feature +++ b/test/e2e/features/install.feature @@ -417,3 +417,29 @@ Feature: Install ClusterExtension """ [{"type":"olm.test-property","value":"some-value"}] """ + + @BoxcutterRuntime + Scenario: ClusterExtensionRevision is labeled with owner information + When ClusterExtension is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterExtension + metadata: + name: ${NAME} + spec: + namespace: ${TEST_NAMESPACE} + serviceAccount: + name: olm-sa + source: + sourceType: Catalog + catalog: + packageName: test + version: 1.2.0 + selector: + matchLabels: + "olm.operatorframework.io/metadata.name": test-catalog + """ + Then ClusterExtension is rolled out + And ClusterExtension is available + And ClusterExtensionRevision "${NAME}-1" has label "olm.operatorframework.io/owner-kind" with value "ClusterExtension" + And ClusterExtensionRevision "${NAME}-1" has label "olm.operatorframework.io/owner-name" with value "${NAME}" diff --git a/test/e2e/steps/steps.go b/test/e2e/steps/steps.go index d6ba67ccf..483514e40 100644 --- a/test/e2e/steps/steps.go +++ b/test/e2e/steps/steps.go @@ -74,6 +74,7 @@ func RegisterSteps(sc *godog.ScenarioContext) { sc.Step(`^(?i)ClusterExtension reports ([[:alnum:]]+) transition between (\d+) and (\d+) minutes since its creation$`, ClusterExtensionReportsConditionTransitionTime) sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" is archived$`, ClusterExtensionRevisionIsArchived) sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" contains annotation "([^"]+)" with value$`, ClusterExtensionRevisionHasAnnotationWithValue) + sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" has label "([^"]+)" with value "([^"]+)"$`, ClusterExtensionRevisionHasLabelWithValue) sc.Step(`^(?i)resource "([^"]+)" is installed$`, ResourceAvailable) sc.Step(`^(?i)resource "([^"]+)" is available$`, ResourceAvailable) @@ -507,6 +508,24 @@ func ClusterExtensionRevisionHasAnnotationWithValue(ctx context.Context, revisio return nil } +func ClusterExtensionRevisionHasLabelWithValue(ctx context.Context, revisionName, labelKey, labelValue string) error { + sc := scenarioCtx(ctx) + revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) + labelValue = substituteScenarioVars(labelValue, sc) + waitFor(ctx, func() bool { + obj, err := getResource("clusterextensionrevision", revisionName, "") + if err != nil { + logger.V(1).Error(err, "failed to get clusterextensionrevision", "name", revisionName) + return false + } + if obj.GetLabels() == nil { + return false + } + return obj.GetLabels()[labelKey] == labelValue + }) + return nil +} + func ResourceAvailable(ctx context.Context, resource string) error { sc := scenarioCtx(ctx) resource = substituteScenarioVars(resource, sc)