From 05adaefa2b9d6be7ee1732528739a49ed221300e Mon Sep 17 00:00:00 2001 From: aeltai Date: Wed, 19 Aug 2026 11:38:18 +0200 Subject: [PATCH] fix: check unprefixed os.unmanaged annotation in reset plan selection When labelPrefix is "-", the operator stores annotations without the elemental.cattle.io/ prefix. The reset controller now also checks the unprefixed "os.unmanaged" key when deciding whether to issue the unmanaged sentinel plan vs the toolkit recovery plan. Fixes: rancher/elemental-operator#1023 Co-authored-by: Cursor --- controllers/machineinventory_controller.go | 7 +++- .../machineinventory_controller_test.go | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/controllers/machineinventory_controller.go b/controllers/machineinventory_controller.go index f653ec82e..610edfb20 100644 --- a/controllers/machineinventory_controller.go +++ b/controllers/machineinventory_controller.go @@ -271,7 +271,12 @@ func (r *MachineInventoryReconciler) updatePlanSecretWithReset(ctx context.Conte networkNeedsReset := mInventory.Spec.Network.Configurator != network.ConfiguratorNone unmanaged, unmanagedFound := mInventory.Annotations[elementalv1.MachineInventoryOSUnmanagedAnnotation] - if unmanagedFound && unmanaged == "true" { + // Backward-compat / labelPrefix edge-case: + // When MachineRegistration.spec.labelPrefix is "-", prefixing of client-sent + // dynamic annotations is disabled, which may result in the operator storing + // the unmanaged flag under the unprefixed key "os.unmanaged". + unmanagedUnprefixed, unmanagedUnprefixedFound := mInventory.Annotations["os.unmanaged"] + if (unmanagedFound && unmanaged == "true") || (unmanagedUnprefixedFound && unmanagedUnprefixed == "true") { checksum, resetPlan, err = r.newUnmanagedResetPlan(ctx) } else { checksum, resetPlan, err = r.newResetPlan(ctx, networkNeedsReset) diff --git a/controllers/machineinventory_controller_test.go b/controllers/machineinventory_controller_test.go index aa73792e6..61e49dd36 100644 --- a/controllers/machineinventory_controller_test.go +++ b/controllers/machineinventory_controller_test.go @@ -754,6 +754,42 @@ var _ = Describe("handle unmanaged finalizer", func() { }, mInventory)).To(Succeed()) }) + It("should update secret with reset plan when unprefixed os.unmanaged annotation is true", func() { + // Remove the prefixed annotation so the test only passes if the + // controller also checks the unprefixed key. + delete(mInventory.Annotations, elementalv1.MachineInventoryOSUnmanagedAnnotation) + + Expect(cl.Get(ctx, client.ObjectKey{ + Name: planSecret.Name, + Namespace: planSecret.Namespace, + }, planSecret)).To(Succeed()) + Expect(cl.Get(ctx, client.ObjectKey{ + Name: mInventory.Name, + Namespace: mInventory.Namespace, + }, mInventory)).To(Succeed()) + + delete(mInventory.Annotations, elementalv1.MachineInventoryOSUnmanagedAnnotation) + mInventory.Annotations["os.unmanaged"] = "true" + Expect(cl.Update(ctx, mInventory)).To(Succeed()) + + _, wantPlan, err := r.newUnmanagedResetPlan(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Check we are holding on the MachineInventory (preventing actual deletion) + _, err = r.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: mInventory.Namespace, + Name: mInventory.Name, + }, + }) + Expect(err).ToNot(HaveOccurred()) + + Expect(planSecret.Annotations[elementalv1.PlanTypeAnnotation]).To(Equal(elementalv1.PlanTypeReset)) + Expect(string(planSecret.Data["plan"])).To(Equal(string(wantPlan))) + Expect(string(planSecret.Data["applied-checksum"])).To(Equal("")) + Expect(string(planSecret.Data["failed-checksum"])).To(Equal("")) + }) + It("should remove finalizer on unmanaged reset plan applied", func() { // 6. Mark the reset plan as applied Expect(cl.Get(ctx, client.ObjectKey{