Skip to content

Commit a2ad950

Browse files
authored
Merge pull request #126 from susesamu/125-label
Enable auto-registration when Rancher Prime is deployed via Helm with registration code
2 parents 9444b08 + 239a6a1 commit a2ad950

5 files changed

Lines changed: 362 additions & 43 deletions

File tree

internal/consts/names.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,8 @@ func OfflineRequestSecretName(namePartIn string) string {
3232
func OfflineCertificateSecretName(namePartIn string) string {
3333
return fmt.Sprintf("%s%s", OfflineCertificateSecretNamePrefix, namePartIn)
3434
}
35+
36+
// SccManagedByValue constructs the SCC managed-by label value in the format "<operator>_secret-broker"
37+
func SccManagedByValue(operatorName string) string {
38+
return fmt.Sprintf("%s_%s", operatorName, ManagedByValueSecretBroker)
39+
}

pkg/controllers/controller.go

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"maps"
87
"slices"
98
"strings"
109
"time"
@@ -154,7 +153,7 @@ func (h *handler) prepareHandler(registrationObj *v1.Registration, rancherURL st
154153
defaultLabels := map[string]string{
155154
consts.LabelSccHash: registrationObj.Labels[consts.LabelSccHash],
156155
consts.LabelNameSuffix: nameSuffixHash,
157-
consts.LabelSccManagedBy: controllerID,
156+
consts.LabelSccManagedBy: consts.SccManagedByValue(initializer.OperatorName.Get()),
158157
consts.LabelK8sManagedBy: initializer.OperatorName.Get(),
159158
}
160159

@@ -195,6 +194,27 @@ func (h *handler) prepareHandler(registrationObj *v1.Registration, rancherURL st
195194
}
196195
}
197196

197+
// mergeLabelsPreservingHelm merges params labels into the secret while preserving existing Helm ownership.
198+
func mergeLabelsPreservingHelm(secret *corev1.Secret, paramsLabels map[string]string) {
199+
// Initialize labels map if nil
200+
if secret.Labels == nil {
201+
secret.Labels = map[string]string{}
202+
}
203+
204+
// Preserve existing app.kubernetes.io/managed-by (e.g., Helm)
205+
existingK8sManagedBy := secret.Labels[consts.LabelK8sManagedBy]
206+
207+
// Merge params labels (which sets k8s managed-by to operator name)
208+
for k, v := range paramsLabels {
209+
secret.Labels[k] = v
210+
}
211+
212+
// Restore the original k8s managed-by if it was set (preserve Helm ownership)
213+
if existingK8sManagedBy != "" {
214+
secret.Labels[consts.LabelK8sManagedBy] = existingK8sManagedBy
215+
}
216+
}
217+
198218
func (h *handler) OnSecretChange(_ string, incomingObj *corev1.Secret) (*corev1.Secret, error) {
199219
if incomingObj == nil || incomingObj.DeletionTimestamp != nil {
200220
return incomingObj, nil
@@ -204,30 +224,34 @@ func (h *handler) OnSecretChange(_ string, incomingObj *corev1.Secret) (*corev1.
204224
return incomingObj, nil
205225
}
206226

207-
// This only applies to the SCC Entrypoint secrets - currently only used for/by Rancher
208-
// This will adopt "unowned" secrets and ignore any that are owned by other operators
209-
if !helpers.ShouldManage(incomingObj, h.options.OperatorName) {
210-
// When the secret has no managedBy label, we should assume ownership I guess?
211-
if !helpers.HasManagedByLabel(incomingObj) {
212-
h.log.Debugf("taking ownership of the unowned entrypoint secret")
213-
prepared := incomingObj.DeepCopy()
214-
prepared = helpers.TakeOwnership(prepared, h.options.OperatorName)
215-
_, updateErr := h.secretRepo.RetryingPatchUpdate(incomingObj, prepared)
216-
if updateErr != nil {
217-
h.log.Errorf("failed to take ownership of secret %s/%s: %v", incomingObj.Namespace, incomingObj.Name, updateErr)
218-
return incomingObj, updateErr
219-
}
227+
// Phase 1: Adoption - take ownership of unmanaged or Helm-managed entrypoint Secrets
228+
if !helpers.HasSccManagedByLabel(incomingObj) && helpers.ShouldAdopt(incomingObj, h.options.OperatorName) {
229+
h.log.Debugf("adopting unmanaged or Helm-managed entrypoint secret")
230+
prepared := incomingObj.DeepCopy()
220231

221-
h.log.Debugf("Secret %s/%s is now managed by %s", incomingObj.Namespace, incomingObj.Name, h.options.OperatorName)
232+
// Set both SCC and k8s managed-by labels (preserves Helm if present)
233+
prepared = helpers.TakeOwnership(prepared, h.options.OperatorName)
222234

223-
return incomingObj, nil
235+
_, updateErr := h.secretRepo.RetryingPatchUpdate(incomingObj, prepared)
236+
if updateErr != nil {
237+
h.log.Errorf("failed to take ownership of secret %s/%s: %v", incomingObj.Namespace, incomingObj.Name, updateErr)
238+
return incomingObj, updateErr
224239
}
225240

226-
managedBy := helpers.GetManagedByValue(incomingObj)
227-
h.log.Debugf("Secret %s/%s is managed by %s not %s, skipping", incomingObj.Namespace, incomingObj.Name, managedBy, h.options.OperatorName)
241+
h.log.Debugf("Secret %s/%s is now managed by %s", incomingObj.Namespace, incomingObj.Name, h.options.OperatorName)
228242
return incomingObj, nil
229243
}
230244

245+
// Phase 2: Validation - skip Secrets not managed by this operator
246+
if !helpers.ShouldManage(incomingObj, h.options.OperatorName) {
247+
sccManagedBy := helpers.GetSccManagedByValue(incomingObj)
248+
k8sManagedBy := helpers.GetManagedByValue(incomingObj)
249+
h.log.Debugf("Secret %s/%s is managed by another tool (k8s: %s, scc: %s), skipping", incomingObj.Namespace, incomingObj.Name, k8sManagedBy, sccManagedBy)
250+
return incomingObj, nil
251+
}
252+
253+
// Phase 3: Processing - handle managed entrypoint Secret
254+
231255
if _, saltOk := incomingObj.GetLabels()[consts.LabelObjectSalt]; !saltOk {
232256
return h.prepareSecretSalt(incomingObj)
233257
}
@@ -247,7 +271,9 @@ func (h *handler) OnSecretChange(_ string, incomingObj *corev1.Secret) (*corev1.
247271
newSecret.Annotations = map[string]string{}
248272
}
249273
newSecret.Annotations[consts.LabelSccLastProcessed] = time.Now().Format(time.RFC3339)
250-
maps.Copy(newSecret.Labels, params.Labels())
274+
275+
// Merge params labels while preserving Helm ownership
276+
mergeLabelsPreservingHelm(newSecret, params.Labels())
251277

252278
_, updateErr := h.secretRepo.RetryingPatchUpdate(incomingObj, newSecret)
253279
if updateErr != nil {
@@ -291,9 +317,8 @@ func (h *handler) OnSecretChange(_ string, incomingObj *corev1.Secret) (*corev1.
291317
}
292318
newSecret.Annotations[consts.LabelSccLastProcessed] = time.Now().Format(time.RFC3339)
293319

294-
labels := incomingObj.Labels
295-
maps.Copy(labels, params.Labels())
296-
newSecret.Labels = labels
320+
// Merge params labels while preserving Helm ownership
321+
mergeLabelsPreservingHelm(newSecret, params.Labels())
297322

298323
if _, err := h.secretRepo.RetryingPatchUpdate(incomingObj, newSecret); err != nil {
299324
return incomingObj, err
@@ -436,6 +461,9 @@ func (h *handler) OnSecretRemove(_ string, incomingObj *corev1.Secret) (*corev1.
436461
return incomingObj, nil
437462
}
438463

464+
// All Secrets (entrypoint, credentials, regcode, offline) must be managed by this operator
465+
// ShouldManage handles both Helm-managed entrypoint Secrets and operator-created Secrets
466+
// with dual labels (app.kubernetes.io/managed-by + scc.cattle.io/managed-by)
439467
if !helpers.ShouldManage(incomingObj, h.options.OperatorName) {
440468
h.log.Debugf("Secret %s/%s is not managed by %s, skipping", incomingObj.Namespace, incomingObj.Name, h.options.OperatorName)
441469
return incomingObj, nil
@@ -468,6 +496,10 @@ func (h *handler) OnSecretRemove(_ string, incomingObj *corev1.Secret) (*corev1.
468496
return incomingObj, nil
469497
}
470498

499+
// Non-entrypoint Secrets: ShouldManage already checked above
500+
// Operator-created Secrets (credentials, regcode, offline) always have both labels:
501+
// app.kubernetes.io/managed-by: rancher-scc-operator (never Helm)
502+
// scc.cattle.io/managed-by: rancher-scc-operator_secret-broker
471503
if lifecycle.SecretHasCredentialsFinalizer(incomingObj) ||
472504
lifecycle.SecretHasRegCodeFinalizer(incomingObj) {
473505
refs := incomingObj.GetOwnerReferences()

pkg/controllers/helpers/ownership.go

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,71 @@ func GetManagedByValue[T metav1.Object](incomingObj T) string {
2121
return objectLabels[consts.LabelK8sManagedBy]
2222
}
2323

24-
// ShouldManage will verify that this operator should manage a given object
24+
// ShouldAdopt checks if this operator should adopt/take ownership of a resource.
25+
// Returns true for unmanaged resources, Helm-managed resources, or resources already managed by this operator.
26+
func ShouldAdopt[T metav1.Object](incomingObj T, expectedManager string) bool {
27+
objectLabels := incomingObj.GetLabels()
28+
managedBy, hasManagedBy := objectLabels[consts.LabelK8sManagedBy]
29+
managedBySCC, hasManagedBySCC := objectLabels[consts.LabelSccManagedBy]
30+
expectedSCCManager := consts.SccManagedByValue(expectedManager)
31+
32+
// Check k8s label if present: must match expectedManager OR be "Helm" (we can adopt from Helm)
33+
k8sOK := !hasManagedBy || managedBy == expectedManager || managedBy == "Helm"
34+
35+
// Check SCC label if present: must match expectedSCCManager
36+
sccOK := !hasManagedBySCC || managedBySCC == expectedSCCManager
37+
38+
return k8sOK && sccOK
39+
}
40+
41+
// ShouldManage checks if this operator already manages a resource.
42+
// Returns true only for resources already managed by this operator (not Helm-only).
2543
func ShouldManage[T metav1.Object](incomingObj T, expectedManager string) bool {
2644
objectLabels := incomingObj.GetLabels()
2745
managedBy, hasManagedBy := objectLabels[consts.LabelK8sManagedBy]
46+
managedBySCC, hasManagedBySCC := objectLabels[consts.LabelSccManagedBy]
47+
expectedSCCManager := consts.SccManagedByValue(expectedManager)
48+
49+
// Check k8s label if present: must match expectedManager (not Helm-only) OR be Helm with matching SCC
50+
k8sOK := !hasManagedBy || managedBy == expectedManager || (managedBy == "Helm" && hasManagedBySCC)
51+
52+
// Check SCC label if present: must match expectedSCCManager
53+
sccOK := !hasManagedBySCC || managedBySCC == expectedSCCManager
2854

29-
return hasManagedBy && managedBy == expectedManager
55+
// At least one of our labels must be present and both must be valid
56+
return (hasManagedBy || hasManagedBySCC) && k8sOK && sccOK
3057
}
3158

32-
// TakeOwnership will set or overwrite the value of the k8s managed-by label
59+
// TakeOwnership sets the k8s and SCC managed-by labels.
60+
// Preserves app.kubernetes.io/managed-by if it's set to "Helm".
3361
func TakeOwnership[T generic.RuntimeMetaObject](incomingObj T, owner string) T {
3462
objectLabels := incomingObj.GetLabels()
3563
if objectLabels == nil {
3664
objectLabels = map[string]string{
3765
consts.LabelK8sManagedBy: owner,
66+
consts.LabelSccManagedBy: consts.SccManagedByValue(owner),
3867
}
3968
} else {
40-
objectLabels[consts.LabelK8sManagedBy] = owner
69+
// Only overwrite k8s managed-by if it's not Helm
70+
if objectLabels[consts.LabelK8sManagedBy] != "Helm" {
71+
objectLabels[consts.LabelK8sManagedBy] = owner
72+
}
73+
objectLabels[consts.LabelSccManagedBy] = consts.SccManagedByValue(owner)
4174
}
4275

4376
incomingObj.SetLabels(objectLabels)
4477
return incomingObj
4578
}
79+
80+
// HasSccManagedByLabel checks if the SCC-specific managed-by label is set
81+
func HasSccManagedByLabel[T metav1.Object](incomingObj T) bool {
82+
objectLabels := incomingObj.GetLabels()
83+
_, hasManagedBy := objectLabels[consts.LabelSccManagedBy]
84+
return hasManagedBy
85+
}
86+
87+
// GetSccManagedByValue returns the value of scc.cattle.io/managed-by
88+
func GetSccManagedByValue[T metav1.Object](incomingObj T) string {
89+
objectLabels := incomingObj.GetLabels()
90+
return objectLabels[consts.LabelSccManagedBy]
91+
}

0 commit comments

Comments
 (0)