From 9cebb6f488a864ec21dfc01114e425c942926efa Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Thu, 11 Dec 2025 16:57:22 +0300 Subject: [PATCH 1/2] fix Signed-off-by: Valeriy Khorunzhin --- .../pkg/controller/indexer/indexer.go | 22 +++++ .../vm/internal/watcher/secret_watcher.go | 93 +++++++++++++++++++ .../pkg/controller/vm/vm_reconciler.go | 1 + 3 files changed, 116 insertions(+) create mode 100644 images/virtualization-artifact/pkg/controller/vm/internal/watcher/secret_watcher.go diff --git a/images/virtualization-artifact/pkg/controller/indexer/indexer.go b/images/virtualization-artifact/pkg/controller/indexer/indexer.go index 078e253114..bc9243b3af 100644 --- a/images/virtualization-artifact/pkg/controller/indexer/indexer.go +++ b/images/virtualization-artifact/pkg/controller/indexer/indexer.go @@ -59,6 +59,8 @@ const ( IndexFieldVMMACLeaseByVMMAC = "spec.virtualMachineMACAddressRef.Name" IndexFieldVMIPLeaseByVMIP = "spec.virtualMachineIPAddressRef" + + IndexFieldVMByProvisioningSecret = "spec.provisioning.secretRef" ) var IndexGetters = []IndexGetter{ @@ -67,6 +69,7 @@ var IndexGetters = []IndexGetter{ IndexVMByVI, IndexVMByCVI, IndexVMByNode, + IndexVMByProvisioningSecret, IndexVMSnapshotByVM, IndexVMSnapshotByVDSnapshot, IndexVMRestoreByVMSnapshot, @@ -134,6 +137,25 @@ func IndexVMByNode() (obj client.Object, field string, extractValue client.Index } } +func IndexVMByProvisioningSecret() (obj client.Object, field string, extractValue client.IndexerFunc) { + return &v1alpha2.VirtualMachine{}, IndexFieldVMByProvisioningSecret, func(object client.Object) []string { + vm, ok := object.(*v1alpha2.VirtualMachine) + if !ok || vm == nil || vm.Spec.Provisioning == nil { + return nil + } + + var secrets []string + if vm.Spec.Provisioning.UserDataRef != nil && vm.Spec.Provisioning.UserDataRef.Kind == v1alpha2.UserDataRefKindSecret { + secrets = append(secrets, vm.Spec.Provisioning.UserDataRef.Name) + } + if vm.Spec.Provisioning.SysprepRef != nil && vm.Spec.Provisioning.SysprepRef.Kind == v1alpha2.SysprepRefKindSecret { + secrets = append(secrets, vm.Spec.Provisioning.SysprepRef.Name) + } + + return secrets + } +} + func getBlockDeviceNamesByKind(obj client.Object, kind v1alpha2.BlockDeviceKind) []string { vm, ok := obj.(*v1alpha2.VirtualMachine) if !ok || vm == nil { diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/watcher/secret_watcher.go b/images/virtualization-artifact/pkg/controller/vm/internal/watcher/secret_watcher.go new file mode 100644 index 0000000000..c2e21e9c7f --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vm/internal/watcher/secret_watcher.go @@ -0,0 +1,93 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package watcher + +import ( + "context" + "fmt" + "log/slog" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + "github.com/deckhouse/virtualization-controller/pkg/controller/indexer" + "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +func NewSecretWatcher(client client.Client) *SecretWatcher { + return &SecretWatcher{ + client: client, + logger: slog.Default().With("watcher", "secret"), + } +} + +type SecretWatcher struct { + client client.Client + logger *slog.Logger +} + +func (w *SecretWatcher) Watch(mgr manager.Manager, ctr controller.Controller) error { + if err := ctr.Watch( + source.Kind( + mgr.GetCache(), + &corev1.Secret{}, + handler.TypedEnqueueRequestsFromMapFunc(w.enqueue), + predicate.TypedFuncs[*corev1.Secret]{ + UpdateFunc: func(e event.TypedUpdateEvent[*corev1.Secret]) bool { + return e.ObjectOld.Type != e.ObjectNew.Type + }, + DeleteFunc: func(e event.TypedDeleteEvent[*corev1.Secret]) bool { return false }, + }, + ), + ); err != nil { + return fmt.Errorf("error setting watch on Secret: %w", err) + } + return nil +} + +func (w *SecretWatcher) enqueue(ctx context.Context, secret *corev1.Secret) []reconcile.Request { + var vms v1alpha2.VirtualMachineList + err := w.client.List(ctx, &vms, &client.ListOptions{ + Namespace: secret.Namespace, + FieldSelector: fields.OneTermEqualSelector(indexer.IndexFieldVMByProvisioningSecret, secret.Name), + }) + if err != nil { + w.logger.Error(fmt.Sprintf("failed to list virtual machines: %v", err)) + return nil + } + + var result []reconcile.Request + for _, vm := range vms.Items { + result = append(result, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: vm.GetName(), + Namespace: vm.GetNamespace(), + }, + }) + } + + return result +} diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 92d0baaa20..ba45da80bd 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -73,6 +73,7 @@ func (r *Reconciler) SetupController(_ context.Context, mgr manager.Manager, ctr watcher.NewVirtualMachineSnapshotWatcher(), watcher.NewVMOPWatcher(), watcher.NewVMMACWatcher(), + watcher.NewSecretWatcher(mgr.GetClient()), } { err := w.Watch(mgr, ctr) if err != nil { From a1fdc6fef8691c2f340d183b4eba118a04018146 Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Thu, 11 Dec 2025 17:05:57 +0300 Subject: [PATCH 2/2] immutable Signed-off-by: Valeriy Khorunzhin --- .../pkg/controller/vm/internal/watcher/secret_watcher.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/watcher/secret_watcher.go b/images/virtualization-artifact/pkg/controller/vm/internal/watcher/secret_watcher.go index c2e21e9c7f..f8684edf7c 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/watcher/secret_watcher.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/watcher/secret_watcher.go @@ -56,9 +56,6 @@ func (w *SecretWatcher) Watch(mgr manager.Manager, ctr controller.Controller) er &corev1.Secret{}, handler.TypedEnqueueRequestsFromMapFunc(w.enqueue), predicate.TypedFuncs[*corev1.Secret]{ - UpdateFunc: func(e event.TypedUpdateEvent[*corev1.Secret]) bool { - return e.ObjectOld.Type != e.ObjectNew.Type - }, DeleteFunc: func(e event.TypedDeleteEvent[*corev1.Secret]) bool { return false }, }, ),