From 58cb7cd4fa9a24a5ea3ac553141a53c049255266 Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Fri, 27 Mar 2026 11:11:48 +0300 Subject: [PATCH] remove cillium ip annotation Signed-off-by: Valeriy Khorunzhin --- .../pkg/controller/kvbuilder/kvvm.go | 18 ++++++++++++++++ .../pkg/controller/kvbuilder/kvvm_utils.go | 3 +++ .../controller/vm/internal/sync_metadata.go | 21 ++++++++++++------- .../vm/internal/sync_metadata_test.go | 17 +++++++++++++++ 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 6c763eb6ef..aadbe93e7f 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -119,6 +119,24 @@ func (b *KVVM) SetKVVMIAnnotation(annoKey, annoValue string) { b.Resource.Spec.Template.ObjectMeta.SetAnnotations(anno) } +// RemoveKVVMIAnnotation deletes an annotation from the VMI template metadata. +func (b *KVVM) RemoveKVVMIAnnotation(annoKey string) { + anno := b.Resource.Spec.Template.ObjectMeta.GetAnnotations() + if len(anno) == 0 { + return + } + if _, ok := anno[annoKey]; !ok { + return + } + next := maps.Clone(anno) + delete(next, annoKey) + if len(next) == 0 { + b.Resource.Spec.Template.ObjectMeta.Annotations = nil + } else { + b.Resource.Spec.Template.ObjectMeta.SetAnnotations(next) + } +} + func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if b.Resource.Spec.Template.Spec.Domain.CPU == nil { b.Resource.Spec.Template.Spec.Domain.CPU = &virtv1.CPU{} diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go index a745c1021e..2f2bb13e63 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go @@ -289,6 +289,9 @@ func ApplyVirtualMachineSpec( if ipAddress != "" { // Set ip address cni request annotation. kvvm.SetKVVMIAnnotation(netmanager.AnnoIPAddressCNIRequest, ipAddress) + } else { + // Drop stale static IP request when the VM no longer has an allocated address. + kvvm.RemoveKVVMIAnnotation(netmanager.AnnoIPAddressCNIRequest) } // Set live migration annotation. diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_metadata.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_metadata.go index 8042091cb8..6343ee9f2a 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_metadata.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_metadata.go @@ -63,6 +63,7 @@ func (h *SyncMetadataHandler) Handle(ctx context.Context, s state.VirtualMachine } current := s.VirtualMachine().Current() + changedVM := s.VirtualMachine().Changed() // Propagate user specified labels and annotations from the d8 VM to kubevirt VM. kvvmNewMetadata := &metav1.ObjectMeta{} @@ -84,7 +85,7 @@ func (h *SyncMetadataHandler) Handle(ctx context.Context, s state.VirtualMachine } if metaUpdated { - if err = h.patchLabelsAndAnnotations(ctx, kvvmi, kvvmiNewMetadata); err != nil && !k8serrors.IsNotFound(err) { + if err = h.patchLabelsAndAnnotations(ctx, kvvmi, kvvmiNewMetadata, changedVM); err != nil && !k8serrors.IsNotFound(err) { return reconcile.Result{}, fmt.Errorf("failed to patch metadata KubeVirt VMI %q: %w", kvvmi.GetName(), err) } } @@ -109,7 +110,7 @@ func (h *SyncMetadataHandler) Handle(ctx context.Context, s state.VirtualMachine } if metaUpdated { - if err = h.patchLabelsAndAnnotations(ctx, &pod, podNewMetadata); err != nil { + if err = h.patchLabelsAndAnnotations(ctx, &pod, podNewMetadata, changedVM); err != nil { return reconcile.Result{}, fmt.Errorf("failed to patch KubeVirt Pod %q: %w", pod.GetName(), err) } } @@ -127,7 +128,7 @@ func (h *SyncMetadataHandler) Handle(ctx context.Context, s state.VirtualMachine } if labelsChanged || annosChanged || kvvmMetaUpdated { - if err = h.patchLabelsAndAnnotations(ctx, kvvm, kvvmNewMetadata); err != nil { + if err = h.patchLabelsAndAnnotations(ctx, kvvm, kvvmNewMetadata, changedVM); err != nil { return reconcile.Result{}, fmt.Errorf("failed to patch metadata KubeVirt VM %q: %w", kvvm.GetName(), err) } } @@ -139,7 +140,7 @@ func (h *SyncMetadataHandler) Name() string { return nameSyncMetadataHandler } -func (h *SyncMetadataHandler) patchLabelsAndAnnotations(ctx context.Context, obj client.Object, metadata *metav1.ObjectMeta) error { +func (h *SyncMetadataHandler) patchLabelsAndAnnotations(ctx context.Context, obj client.Object, metadata *metav1.ObjectMeta, vm *v1alpha2.VirtualMachine) error { jp := patch.NewJSONPatch() newLabels := metadata.GetLabels() @@ -172,7 +173,7 @@ func (h *SyncMetadataHandler) patchLabelsAndAnnotations(ctx context.Context, obj ) if objIsKVVM { currSpecTemplateAnno := kvvm.Spec.Template.ObjectMeta.Annotations - syncedSpecTemplateAnno := h.updateKVVMSpecTemplateMetadataAnnotations(currSpecTemplateAnno, newAnnotations) + syncedSpecTemplateAnno := h.updateKVVMSpecTemplateMetadataAnnotations(currSpecTemplateAnno, newAnnotations, vm) jp.Append( patch.WithTest("/spec/template/metadata/annotations", currSpecTemplateAnno), patch.WithReplace("/spec/template/metadata/annotations", syncedSpecTemplateAnno), @@ -190,7 +191,8 @@ func (h *SyncMetadataHandler) patchLabelsAndAnnotations(ctx context.Context, obj // updateKVVMSpecTemplateMetadataAnnotations ensures that the special network annotation is present if it exists. // It also removes well-known annotations that are dangerous to propagate. -func (h *SyncMetadataHandler) updateKVVMSpecTemplateMetadataAnnotations(currAnno, newAnno map[string]string) map[string]string { +// Static Cilium IP is preserved only while the VM status still tracks a VirtualMachineIPAddress / address. +func (h *SyncMetadataHandler) updateKVVMSpecTemplateMetadataAnnotations(currAnno, newAnno map[string]string, vm *v1alpha2.VirtualMachine) map[string]string { res := make(map[string]string, len(newAnno)) for k, v := range newAnno { if k == annotations.AnnVMLastAppliedSpec || k == annotations.AnnVMClassLastAppliedSpec { @@ -208,8 +210,11 @@ func (h *SyncMetadataHandler) updateKVVMSpecTemplateMetadataAnnotations(currAnno res[virtv1.AllowPodBridgeNetworkLiveMigrationAnnotation] = v } - if v, ok := currAnno[netmanager.AnnoIPAddressCNIRequest]; ok { - res[netmanager.AnnoIPAddressCNIRequest] = v + if v, ok := currAnno[netmanager.AnnoIPAddressCNIRequest]; ok && vm != nil { + st := vm.Status + if st.VirtualMachineIPAddress != "" || st.IPAddress != "" { + res[netmanager.AnnoIPAddressCNIRequest] = v + } } return commonvm.RemoveNonPropagatableAnnotations(res) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_metadata_test.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_metadata_test.go index cc4be44639..6d920c3efd 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_metadata_test.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_metadata_test.go @@ -179,6 +179,9 @@ var _ = Describe("SyncMetadataHandler", func() { Describe("Propagating VM metadata to KVVM, KVVMI and Pod", func() { It("handles a virtual machine metadata updating", func() { vm := newVM() + // Cilium static IP on the template is kept only while the VM tracks IPAM in status. + vm.Status.VirtualMachineIPAddress = "vmip" + vm.Status.IPAddress = ipAddressAnnoValue kvvm := newKVVM(vm) kvvmi := newEmptyKVVMI(name, namespace) pod := newEmptyPOD(name, namespace, vm.Name) @@ -219,5 +222,19 @@ var _ = Describe("SyncMetadataHandler", func() { validateObjResourceStatusLabels(pod, vm) }) }) + + It("drops stale Cilium IP annotation from KVVM template when VM has no IPAM status", func() { + vm := newVM() + kvvm := newKVVM(vm) + + fakeClient, _, vmState = setupEnvironment(vm, kvvm) + h := NewSyncMetadataHandler(fakeClient) + _, err := h.Handle(ctx, vmState) + Expect(err).NotTo(HaveOccurred()) + + err = fakeClient.Get(context.Background(), client.ObjectKey{Namespace: namespace, Name: vm.Name}, kvvm) + Expect(err).NotTo(HaveOccurred()) + Expect(kvvm.Spec.Template.ObjectMeta.Annotations).ToNot(HaveKey(netmanager.AnnoIPAddressCNIRequest)) + }) }) })