Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -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)
}
}
Expand All @@ -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)
}
}
Expand All @@ -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)
}
}
Expand All @@ -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()
Expand Down Expand Up @@ -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),
Expand All @@ -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 {
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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))
})
})
})
Loading