From 5db859d4c8dd7566f25bae42dc97f9b2e962b735 Mon Sep 17 00:00:00 2001 From: savio87 Date: Thu, 13 Aug 2026 13:04:02 +0530 Subject: [PATCH] OCPBUGS-39539: clear rogue controller ownerReferences during merge --- lib/resourcemerge/meta.go | 19 ++++++++++++++ lib/resourcemerge/meta_test.go | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/lib/resourcemerge/meta.go b/lib/resourcemerge/meta.go index e23f5ab479..77a74bacd7 100644 --- a/lib/resourcemerge/meta.go +++ b/lib/resourcemerge/meta.go @@ -73,4 +73,23 @@ func mergeOwnerRefs(modified *bool, existing *[]metav1.OwnerReference, required *existing = append(*existing, required[ridx]) } } + + // If a required ref claims Controller=true, clear Controller on any + // existing refs not in the required set to avoid the API server + // rejecting the update with "only one reference can have Controller + // set to true". + for ridx := range required { + if required[ridx].Controller == nil || !*required[ridx].Controller { + continue + } + for eidx := range *existing { + if (*existing)[eidx].UID == required[ridx].UID { + continue + } + if (*existing)[eidx].Controller != nil && *(*existing)[eidx].Controller { + *modified = true + (*existing)[eidx].Controller = nil + } + } + } } diff --git a/lib/resourcemerge/meta_test.go b/lib/resourcemerge/meta_test.go index 2e652c04e3..e3b57d0021 100644 --- a/lib/resourcemerge/meta_test.go +++ b/lib/resourcemerge/meta_test.go @@ -100,6 +100,52 @@ func TestMergeOwnerRefs(t *testing.T) { Controller: ptr.To(true), UID: types.UID("uid-1"), }}, + }, { + // Rogue controller ref on existing should be cleared when + // a required ref claims Controller=true (OCPBUGS-39539). + existing: []metav1.OwnerReference{{ + Kind: "ClusterServiceVersion", + Name: "rogue-operator.1.0.0", + Controller: ptr.To(true), + UID: types.UID("uid-rogue"), + }}, + input: []metav1.OwnerReference{{ + Kind: "ClusterVersion", + Name: "version", + Controller: ptr.To(true), + UID: types.UID("uid-cv"), + }}, + + expectedModified: true, + expected: []metav1.OwnerReference{{ + Kind: "ClusterServiceVersion", + Name: "rogue-operator.1.0.0", + UID: types.UID("uid-rogue"), + }, { + Kind: "ClusterVersion", + Name: "version", + Controller: ptr.To(true), + UID: types.UID("uid-cv"), + }}, + }, { + // Non-controller existing ref should not be modified. + existing: []metav1.OwnerReference{{ + Kind: "SomeOther", + UID: types.UID("uid-other"), + }}, + input: []metav1.OwnerReference{{ + Controller: ptr.To(true), + UID: types.UID("uid-cv"), + }}, + + expectedModified: true, + expected: []metav1.OwnerReference{{ + Kind: "SomeOther", + UID: types.UID("uid-other"), + }, { + Controller: ptr.To(true), + UID: types.UID("uid-cv"), + }}, }} for idx, test := range tests {