@@ -114,6 +114,13 @@ func (r *DefaultsApplierReconciler) applyResource(ctx context.Context, desired *
114114 // does not end up in our managed field set.
115115 unstructured .RemoveNestedField (desired .Object , "status" )
116116
117+ // When adopting a resource that was Helm-managed before the 1.37 chart unification,
118+ // strip the Helm bookkeeping annotations from the live object. This must happen
119+ // before the server-side Apply below, while the live object still reports Helm ownership.
120+ if err := r .stripHelmBookkeeping (ctx , desired ); err != nil {
121+ return err
122+ }
123+
117124 // Stamp the ownership label used by cleanupStale to identify managed resources.
118125 labels := desired .GetLabels ()
119126 if labels == nil {
@@ -133,6 +140,66 @@ func (r *DefaultsApplierReconciler) applyResource(ctx context.Context, desired *
133140 return nil
134141}
135142
143+ // stripHelmBookkeeping removes Helm bookkeeping annotations from the live resource when it
144+ // was previously Helm-managed and the desired state explicitly claims controller ownership.
145+ //
146+ // This handles the 1.37 migration where the default PolicyServer and recommended policies
147+ // moved from Helm ownership to controller ownership. Requiring both conditions avoids
148+ // stripping annotations from resources that carry managed-by: Helm for unrelated reasons.
149+ //
150+ // The Helm annotations cannot be removed by the server-side apply in applyResource: they are
151+ // owned by Helm's field manager and are absent from our applied configuration,
152+ // so the API server leaves them untouched. A dedicated merge patch is required.
153+ func (r * DefaultsApplierReconciler ) stripHelmBookkeeping (ctx context.Context , desired * unstructured.Unstructured ) error {
154+ if desired .GetLabels ()[constants .ManagedByKey ] != constants .ManagedByKeyLabelValue {
155+ // exit if we are not taking ownership
156+ return nil
157+ }
158+
159+ live := & unstructured.Unstructured {}
160+ live .SetGroupVersionKind (desired .GroupVersionKind ())
161+ if err := r .Get (ctx , client .ObjectKeyFromObject (desired ), live ); err != nil {
162+ if apierrors .IsNotFound (err ) {
163+ // fresh resource: nothing to strip
164+ return nil
165+ }
166+ return fmt .Errorf ("failed to get live resource: %w" , err )
167+ }
168+
169+ if live .GetLabels ()[constants .ManagedByKey ] != "Helm" {
170+ // exit if it wasn't previously owned by Helm
171+ return nil
172+ }
173+
174+ orig := live .DeepCopy ()
175+ annotations := live .GetAnnotations ()
176+ changed := false
177+ helmBookkeepingAnnotations := []string {
178+ constants .HelmResourcePolicy ,
179+ constants .HelmMetaReleaseName ,
180+ constants .HelmMetaReleaseNamespace ,
181+ }
182+ for _ , key := range helmBookkeepingAnnotations {
183+ if _ , found := annotations [key ]; found {
184+ delete (annotations , key )
185+ changed = true
186+ }
187+ }
188+ if ! changed {
189+ // exit if there was no annotations to remove
190+ return nil
191+ }
192+ live .SetAnnotations (annotations )
193+
194+ if err := r .Patch (ctx , live , client .MergeFrom (orig )); err != nil {
195+ return fmt .Errorf ("failed to strip Helm annotations: %w" , err )
196+ }
197+
198+ r .Log .Info ("Stripped Helm bookkeeping annotations from adopted resource" ,
199+ "resource" , client .ObjectKeyFromObject (live ), "kind" , live .GetKind ())
200+ return nil
201+ }
202+
136203// isSupportedGVK returns true if the GroupVersionKind is a known Kubewarden resource type.
137204func isSupportedGVK (gvk schema.GroupVersionKind ) bool {
138205 if gvk .Group != constants .KubewardenPoliciesGroup {
0 commit comments