From ef071683aae962cda8dde70cf3c000de17f5ece0 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Wed, 9 Oct 2024 18:54:44 +0200 Subject: [PATCH] feat: Added bi-directional annotation and label syncing Signed-off-by: Thomas Kosiewski --- .../resources/configmaps/syncer.go | 39 +++++++-------- .../resources/csistoragecapacities/syncer.go | 2 +- pkg/controllers/resources/endpoints/syncer.go | 8 ++++ .../resources/endpoints/translate.go | 3 -- pkg/controllers/resources/ingresses/syncer.go | 2 +- .../resources/ingresses/translate.go | 15 ++++-- .../resources/namespaces/syncer.go | 2 +- .../resources/networkpolicies/syncer.go | 9 ++++ .../resources/networkpolicies/translate.go | 3 -- .../persistentvolumeclaims/syncer.go | 9 +--- .../persistentvolumeclaims/translate.go | 14 +++--- .../resources/persistentvolumes/syncer.go | 9 +++- .../persistentvolumes/syncer_test.go | 28 +++++++++-- .../resources/poddisruptionbudgets/syncer.go | 9 ++++ .../poddisruptionbudgets/translate.go | 2 - .../resources/pods/translate/diff.go | 48 ++++++++++--------- .../resources/runtimeclasses/syncer.go | 4 +- pkg/controllers/resources/secrets/syncer.go | 8 ++-- .../resources/serviceaccounts/syncer.go | 3 +- pkg/controllers/resources/services/syncer.go | 9 ++-- .../resources/storageclasses/syncer.go | 9 +++- .../volumesnapshotcontents/syncer.go | 6 +++ .../volumesnapshotcontents/syncer_test.go | 34 ++++++++++--- .../resources/volumesnapshots/syncer.go | 9 +++- pkg/syncer/testing/testing.go | 4 +- pkg/syncer/wrapper.go | 15 ++++-- 26 files changed, 192 insertions(+), 111 deletions(-) diff --git a/pkg/controllers/resources/configmaps/syncer.go b/pkg/controllers/resources/configmaps/syncer.go index 57a2427c2b..5ff04fd677 100644 --- a/pkg/controllers/resources/configmaps/syncer.go +++ b/pkg/controllers/resources/configmaps/syncer.go @@ -60,10 +60,8 @@ func (s *configMapSyncer) ModifyController(ctx *synccontext.RegisterContext, bui } func (s *configMapSyncer) SyncToHost(ctx *synccontext.SyncContext, event *synccontext.SyncToHostEvent[*corev1.ConfigMap]) (ctrl.Result, error) { - createNeeded, err := s.isConfigMapUsed(ctx, event.Virtual) - if err != nil { - return ctrl.Result{}, err - } else if !createNeeded { + createNeeded := s.isConfigMapUsed(ctx, event.Virtual) + if !createNeeded { return ctrl.Result{}, nil } @@ -72,7 +70,7 @@ func (s *configMapSyncer) SyncToHost(ctx *synccontext.SyncContext, event *syncco } pObj := translate.HostMetadata(event.Virtual, s.VirtualToHost(ctx, types.NamespacedName{Name: event.Virtual.Name, Namespace: event.Virtual.Namespace}, event.Virtual)) - err = pro.ApplyPatchesHostObject(ctx, nil, pObj, event.Virtual, ctx.Config.Sync.ToHost.ConfigMaps.Patches) + err := pro.ApplyPatchesHostObject(ctx, nil, pObj, event.Virtual, ctx.Config.Sync.ToHost.ConfigMaps.Patches) if err != nil { return ctrl.Result{}, err } @@ -87,14 +85,12 @@ func (s *configMapSyncer) SyncToVirtual(ctx *synccontext.SyncContext, event *syn } vObj := translate.VirtualMetadata(event.Host, s.HostToVirtual(ctx, types.NamespacedName{Name: event.Host.Name, Namespace: event.Host.Namespace}, event.Host)) - createNeeded, err := s.isConfigMapUsed(ctx, vObj) - if err != nil { - return ctrl.Result{}, err - } else if !createNeeded { + createNeeded := s.isConfigMapUsed(ctx, vObj) + if !createNeeded { return ctrl.Result{}, nil } - err = pro.ApplyPatchesVirtualObject(ctx, nil, vObj, event.Host, ctx.Config.Sync.ToHost.ConfigMaps.Patches) + err := pro.ApplyPatchesVirtualObject(ctx, nil, vObj, event.Host, ctx.Config.Sync.ToHost.ConfigMaps.Patches) if err != nil { return ctrl.Result{}, err } @@ -103,12 +99,10 @@ func (s *configMapSyncer) SyncToVirtual(ctx *synccontext.SyncContext, event *syn } func (s *configMapSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext.SyncEvent[*corev1.ConfigMap]) (_ ctrl.Result, retErr error) { - used, err := s.isConfigMapUsed(ctx, event.Virtual) - if err != nil { - return ctrl.Result{}, err - } else if !used { + used := s.isConfigMapUsed(ctx, event.Virtual) + if !used { ctx.Log.Infof("delete physical config map %s/%s, because it is not used anymore", event.Host.GetNamespace(), event.Host.GetName()) - err = ctx.PhysicalClient.Delete(ctx, event.Host) + err := ctx.PhysicalClient.Delete(ctx, event.Host) if err != nil { ctx.Log.Infof("error deleting physical object %s/%s in physical cluster: %v", event.Host.GetNamespace(), event.Host.GetName(), err) return ctrl.Result{}, err @@ -131,13 +125,12 @@ func (s *configMapSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext. } }() - // check annotations & labels - event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) - - // check labels + // bi-directional sync of annotations and labels if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual) event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) } @@ -147,11 +140,11 @@ func (s *configMapSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext. return ctrl.Result{}, nil } -func (s *configMapSyncer) isConfigMapUsed(ctx *synccontext.SyncContext, vObj *corev1.ConfigMap) (bool, error) { +func (s *configMapSyncer) isConfigMapUsed(ctx *synccontext.SyncContext, vObj *corev1.ConfigMap) bool { if vObj.Annotations[constants.SyncResourceAnnotation] == "true" { - return true, nil + return true } else if ctx.Config.Sync.ToHost.ConfigMaps.All { - return true, nil + return true } // retrieve references for config map @@ -163,5 +156,5 @@ func (s *configMapSyncer) isConfigMapUsed(ctx *synccontext.SyncContext, vObj *co }, }) - return len(references) > 0, nil + return len(references) > 0 } diff --git a/pkg/controllers/resources/csistoragecapacities/syncer.go b/pkg/controllers/resources/csistoragecapacities/syncer.go index fb786dcc40..5ee36fb0c1 100644 --- a/pkg/controllers/resources/csistoragecapacities/syncer.go +++ b/pkg/controllers/resources/csistoragecapacities/syncer.go @@ -147,7 +147,7 @@ func (s *csistoragecapacitySyncer) enqueuePhysical(ctx *synccontext.SyncContext, return } - name := s.Mapper.HostToVirtual(ctx, types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, obj) + name := s.HostToVirtual(ctx, types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, obj) if name.Name != "" && name.Namespace != "" { q.Add(reconcile.Request{NamespacedName: name}) } diff --git a/pkg/controllers/resources/endpoints/syncer.go b/pkg/controllers/resources/endpoints/syncer.go index 904cf0e96e..fe34e1b7dd 100644 --- a/pkg/controllers/resources/endpoints/syncer.go +++ b/pkg/controllers/resources/endpoints/syncer.go @@ -82,6 +82,14 @@ func (s *endpointsSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext. return ctrl.Result{}, err } + if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual, s.excludedAnnotations...) + event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) + } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host, s.excludedAnnotations...) + event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) + } + return ctrl.Result{}, nil } diff --git a/pkg/controllers/resources/endpoints/translate.go b/pkg/controllers/resources/endpoints/translate.go index 44c111a373..129b2a74a1 100644 --- a/pkg/controllers/resources/endpoints/translate.go +++ b/pkg/controllers/resources/endpoints/translate.go @@ -49,8 +49,5 @@ func (s *endpointsSyncer) translateUpdate(ctx *synccontext.SyncContext, pObj, vO s.translateSpec(ctx, translated) pObj.Subsets = translated.Subsets - // check annotations & labels - pObj.Annotations = translate.HostAnnotations(vObj, pObj, s.excludedAnnotations...) - pObj.Labels = translate.HostLabels(vObj, pObj) return nil } diff --git a/pkg/controllers/resources/ingresses/syncer.go b/pkg/controllers/resources/ingresses/syncer.go index 256d91c80d..c1de22cfd3 100644 --- a/pkg/controllers/resources/ingresses/syncer.go +++ b/pkg/controllers/resources/ingresses/syncer.go @@ -80,7 +80,7 @@ func (s *ingressSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext.Sy event.TargetObject().Spec.IngressClassName = event.SourceObject().Spec.IngressClassName event.Virtual.Status = event.Host.Status - s.translateUpdate(ctx, event.Host, event.Virtual) + s.translateUpdate(ctx, event.Source, event.Host, event.Virtual) return ctrl.Result{}, nil } diff --git a/pkg/controllers/resources/ingresses/translate.go b/pkg/controllers/resources/ingresses/translate.go index 9fa3638b21..65f8d7338e 100644 --- a/pkg/controllers/resources/ingresses/translate.go +++ b/pkg/controllers/resources/ingresses/translate.go @@ -40,13 +40,18 @@ func (s *ingressSyncer) TranslateMetadataUpdate(ctx *synccontext.SyncContext, vO return translate.HostAnnotations(vIngress, pObj), translate.HostLabels(vIngress, pObj) } -func (s *ingressSyncer) translateUpdate(ctx *synccontext.SyncContext, pObj, vObj *networkingv1.Ingress) { +func (s *ingressSyncer) translateUpdate(ctx *synccontext.SyncContext, source synccontext.SyncEventSource, pObj, vObj *networkingv1.Ingress) { pObj.Spec = *translateSpec(ctx, vObj.Namespace, &vObj.Spec) - var translatedAnnotations map[string]string - translatedAnnotations, pObj.Labels = s.TranslateMetadataUpdate(ctx, vObj, pObj) - translatedAnnotations, _ = resources.TranslateIngressAnnotations(ctx, translatedAnnotations, vObj.Namespace) - pObj.Annotations = translatedAnnotations + if source == synccontext.SyncEventSourceHost { + vObj.Annotations = translate.VirtualAnnotations(pObj, vObj) + vObj.Labels = translate.VirtualLabels(pObj, vObj) + } else { + var translatedAnnotations map[string]string + translatedAnnotations, pObj.Labels = s.TranslateMetadataUpdate(ctx, vObj, pObj) + translatedAnnotations, _ = resources.TranslateIngressAnnotations(ctx, translatedAnnotations, vObj.Namespace) + pObj.Annotations = translatedAnnotations + } } func translateSpec(ctx *synccontext.SyncContext, namespace string, vIngressSpec *networkingv1.IngressSpec) *networkingv1.IngressSpec { diff --git a/pkg/controllers/resources/namespaces/syncer.go b/pkg/controllers/resources/namespaces/syncer.go index 81acdae13e..6807493b74 100644 --- a/pkg/controllers/resources/namespaces/syncer.go +++ b/pkg/controllers/resources/namespaces/syncer.go @@ -63,7 +63,7 @@ type namespaceSyncer struct { var _ syncertypes.Syncer = &namespaceSyncer{} func (s *namespaceSyncer) Syncer() syncertypes.Sync[client.Object] { - return syncer.ToGenericSyncer[*corev1.Namespace](s) + return syncer.ToGenericSyncer(s) } func (s *namespaceSyncer) SyncToHost(ctx *synccontext.SyncContext, event *synccontext.SyncToHostEvent[*corev1.Namespace]) (ctrl.Result, error) { diff --git a/pkg/controllers/resources/networkpolicies/syncer.go b/pkg/controllers/resources/networkpolicies/syncer.go index 0d98ba60e0..9c08aab7c7 100644 --- a/pkg/controllers/resources/networkpolicies/syncer.go +++ b/pkg/controllers/resources/networkpolicies/syncer.go @@ -67,6 +67,15 @@ func (s *networkPolicySyncer) Sync(ctx *synccontext.SyncContext, event *synccont }() s.translateUpdate(event.Host, event.Virtual) + + if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual) + event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) + } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) + event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) + } + return ctrl.Result{}, nil } diff --git a/pkg/controllers/resources/networkpolicies/translate.go b/pkg/controllers/resources/networkpolicies/translate.go index bd130b8cfc..3f65968379 100644 --- a/pkg/controllers/resources/networkpolicies/translate.go +++ b/pkg/controllers/resources/networkpolicies/translate.go @@ -19,9 +19,6 @@ func (s *networkPolicySyncer) translateUpdate(pObj, vObj *networkingv1.NetworkPo if translatedSpec := translateSpec(&vObj.Spec, vObj.GetNamespace()); translatedSpec != nil { pObj.Spec = *translatedSpec } - - pObj.Annotations = translate.HostAnnotations(vObj, pObj) - pObj.Labels = translate.HostLabels(vObj, pObj) } func translateSpec(spec *networkingv1.NetworkPolicySpec, namespace string) *networkingv1.NetworkPolicySpec { diff --git a/pkg/controllers/resources/persistentvolumeclaims/syncer.go b/pkg/controllers/resources/persistentvolumeclaims/syncer.go index 1fce293737..7d7c9094ea 100644 --- a/pkg/controllers/resources/persistentvolumeclaims/syncer.go +++ b/pkg/controllers/resources/persistentvolumeclaims/syncer.go @@ -171,17 +171,12 @@ func (s *persistentVolumeClaimSyncer) Sync(ctx *synccontext.SyncContext, event * // allow storage size to be increased event.Host.Spec.Resources.Requests = event.Virtual.Spec.Resources.Requests - // change annotations + // bi-directional sync of annotations and labels if event.Source == synccontext.SyncEventSourceHost { event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual, s.excludedAnnotations...) - } else { - event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host, s.excludedAnnotations...) - } - - // check labels - if event.Source == synccontext.SyncEventSourceHost { event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host, s.excludedAnnotations...) event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) } diff --git a/pkg/controllers/resources/persistentvolumeclaims/translate.go b/pkg/controllers/resources/persistentvolumeclaims/translate.go index bf44cb6d09..ad81c8b467 100644 --- a/pkg/controllers/resources/persistentvolumeclaims/translate.go +++ b/pkg/controllers/resources/persistentvolumeclaims/translate.go @@ -11,9 +11,7 @@ import ( "k8s.io/apimachinery/pkg/types" ) -var ( - deprecatedStorageClassAnnotation = "volume.beta.kubernetes.io/storage-class" -) +var deprecatedStorageClassAnnotation = "volume.beta.kubernetes.io/storage-class" func (s *persistentVolumeClaimSyncer) translate(ctx *synccontext.SyncContext, vPvc *corev1.PersistentVolumeClaim) (*corev1.PersistentVolumeClaim, error) { pPVC := translate.HostMetadata(vPvc, s.VirtualToHost(ctx, types.NamespacedName{Name: vPvc.GetName(), Namespace: vPvc.GetNamespace()}, vPvc), s.excludedAnnotations...) @@ -21,9 +19,10 @@ func (s *persistentVolumeClaimSyncer) translate(ctx *synccontext.SyncContext, vP if vPvc.Annotations[constants.SkipTranslationAnnotation] != "true" { if pPVC.Spec.DataSource != nil { - if pPVC.Spec.DataSource.Kind == "VolumeSnapshot" { + switch pPVC.Spec.DataSource.Kind { + case "VolumeSnapshot": pPVC.Spec.DataSource.Name = mappings.VirtualToHostName(ctx, pPVC.Spec.DataSource.Name, vPvc.Namespace, mappings.VolumeSnapshots()) - } else if pPVC.Spec.DataSource.Kind == "PersistentVolumeClaim" { + case "PersistentVolumeClaim": pPVC.Spec.DataSource.Name = mappings.VirtualToHostName(ctx, pPVC.Spec.DataSource.Name, vPvc.Namespace, mappings.PersistentVolumeClaims()) } } @@ -34,9 +33,10 @@ func (s *persistentVolumeClaimSyncer) translate(ctx *synccontext.SyncContext, vP namespace = *pPVC.Spec.DataSourceRef.Namespace } - if pPVC.Spec.DataSourceRef.Kind == "VolumeSnapshot" { + switch pPVC.Spec.DataSourceRef.Kind { + case "VolumeSnapshot": pPVC.Spec.DataSourceRef.Name = mappings.VirtualToHostName(ctx, pPVC.Spec.DataSourceRef.Name, namespace, mappings.VolumeSnapshots()) - } else if pPVC.Spec.DataSourceRef.Kind == "PersistentVolumeClaim" { + case "PersistentVolumeClaim": pPVC.Spec.DataSourceRef.Name = mappings.VirtualToHostName(ctx, pPVC.Spec.DataSourceRef.Name, namespace, mappings.PersistentVolumeClaims()) } } diff --git a/pkg/controllers/resources/persistentvolumes/syncer.go b/pkg/controllers/resources/persistentvolumes/syncer.go index f87b72a11c..f73e0e0739 100644 --- a/pkg/controllers/resources/persistentvolumes/syncer.go +++ b/pkg/controllers/resources/persistentvolumes/syncer.go @@ -141,7 +141,7 @@ func (s *persistentVolumeSyncer) Sync(ctx *synccontext.SyncContext, event *syncc if event.Virtual.GetDeletionTimestamp() != nil { if event.Host.GetDeletionTimestamp() == nil { // check if the PV is dynamically provisioned and the reclaim policy is Delete - if !(event.Virtual.Spec.ClaimRef != nil && event.Virtual.Spec.PersistentVolumeReclaimPolicy == corev1.PersistentVolumeReclaimDelete) { + if event.Virtual.Spec.ClaimRef == nil || event.Virtual.Spec.PersistentVolumeReclaimPolicy != corev1.PersistentVolumeReclaimDelete { ctx.Log.Infof("delete physical persistent volume %s, because virtual persistent volume is deleted", event.Host.GetName()) err := ctx.PhysicalClient.Delete(ctx, event.Host) if err != nil { @@ -212,6 +212,13 @@ func (s *persistentVolumeSyncer) Sync(ctx *synccontext.SyncContext, event *syncc if event.Virtual.Annotations[constants.HostClusterPersistentVolumeAnnotation] == "" { // TODO: translate the storage secrets event.Host.Spec.StorageClassName = mappings.VirtualToHostName(ctx, event.Virtual.Spec.StorageClassName, "", mappings.StorageClasses()) + } + + // bi-directional sync of annotations and labels + if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual, s.excludedAnnotations...) + event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) + } else { event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host, s.excludedAnnotations...) event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) } diff --git a/pkg/controllers/resources/persistentvolumes/syncer_test.go b/pkg/controllers/resources/persistentvolumes/syncer_test.go index ac0ed31403..2f304fa258 100644 --- a/pkg/controllers/resources/persistentvolumes/syncer_test.go +++ b/pkg/controllers/resources/persistentvolumes/syncer_test.go @@ -49,6 +49,16 @@ func TestSync(t *testing.T) { constants.HostClusterPersistentVolumeAnnotation: "testpv", }, } + backwardPvObjectMeta := metav1.ObjectMeta{ + Name: "testpv", + Annotations: map[string]string{ + constants.HostClusterPersistentVolumeAnnotation: "testpv", + translate.HostNameAnnotation: "testpv", + translate.KindAnnotation: "/v1, Kind=PersistentVolume", + translate.NameAnnotation: "testpv", + translate.UIDAnnotation: "", + }, + } basePvWithDelTSObjectMeta := metav1.ObjectMeta{ Name: "testpv", Finalizers: []string{"kubernetes"}, @@ -86,7 +96,7 @@ func TestSync(t *testing.T) { }, } backwardUpdatePPv := &corev1.PersistentVolume{ - ObjectMeta: basePvObjectMeta, + ObjectMeta: backwardPvObjectMeta, Spec: corev1.PersistentVolumeSpec{ ClaimRef: basePPvcReference, StorageClassName: "someStorageClass", @@ -176,7 +186,7 @@ func TestSync(t *testing.T) { }, } backwardRetainPPv := &corev1.PersistentVolume{ - ObjectMeta: basePvObjectMeta, + ObjectMeta: backwardPvObjectMeta, Spec: corev1.PersistentVolumeSpec{ PersistentVolumeReclaimPolicy: corev1.PersistentVolumeReclaimRetain, ClaimRef: &corev1.ObjectReference{ @@ -190,10 +200,12 @@ func TestSync(t *testing.T) { }, } - syncertesting.RunTestsWithContext(t, func(vConfig *config.VirtualClusterConfig, pClient *testingutil.FakeIndexClient, vClient *testingutil.FakeIndexClient) *synccontext.RegisterContext { + createContext := func(vConfig *config.VirtualClusterConfig, pClient *testingutil.FakeIndexClient, vClient *testingutil.FakeIndexClient) *synccontext.RegisterContext { vConfig.Sync.ToHost.PersistentVolumes.Enabled = true return syncertesting.NewFakeRegisterContext(vConfig, pClient, vClient) - }, []*syncertesting.SyncTest{ + } + + testCases := []*syncertesting.SyncTest{ { Name: "Create Backward", InitialVirtualState: []runtime.Object{basePvc}, @@ -456,5 +468,11 @@ func TestSync(t *testing.T) { assert.NilError(t, err) }, }, - }) + } + + for _, test := range testCases { + t.Run(test.Name, func(t *testing.T) { + test.Run(t, createContext) + }) + } } diff --git a/pkg/controllers/resources/poddisruptionbudgets/syncer.go b/pkg/controllers/resources/poddisruptionbudgets/syncer.go index 12431af916..cf24ced8e6 100644 --- a/pkg/controllers/resources/poddisruptionbudgets/syncer.go +++ b/pkg/controllers/resources/poddisruptionbudgets/syncer.go @@ -68,6 +68,15 @@ func (s *pdbSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext.SyncEv }() s.translateUpdate(event.Host, event.Virtual) + + if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual) + event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) + } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) + event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) + } + return ctrl.Result{}, nil } diff --git a/pkg/controllers/resources/poddisruptionbudgets/translate.go b/pkg/controllers/resources/poddisruptionbudgets/translate.go index 32b151a259..61dfac4a79 100644 --- a/pkg/controllers/resources/poddisruptionbudgets/translate.go +++ b/pkg/controllers/resources/poddisruptionbudgets/translate.go @@ -14,8 +14,6 @@ func (s *pdbSyncer) translate(ctx *synccontext.SyncContext, vObj *policyv1.PodDi } func (s *pdbSyncer) translateUpdate(pObj, vObj *policyv1.PodDisruptionBudget) { - pObj.Annotations = translate.HostAnnotations(vObj, pObj) - pObj.Labels = translate.HostLabels(vObj, pObj) pObj.Spec.MaxUnavailable = vObj.Spec.MaxUnavailable pObj.Spec.MinAvailable = vObj.Spec.MinAvailable pObj.Spec.Selector = translate.HostLabelSelector(vObj.Spec.Selector) diff --git a/pkg/controllers/resources/pods/translate/diff.go b/pkg/controllers/resources/pods/translate/diff.go index 255c49b992..c82a6dfd57 100644 --- a/pkg/controllers/resources/pods/translate/diff.go +++ b/pkg/controllers/resources/pods/translate/diff.go @@ -23,7 +23,7 @@ func (t *translator) Diff(ctx *synccontext.SyncContext, event *synccontext.SyncE // get Namespace resource in order to have access to its labels vNamespace := &corev1.Namespace{} - err := t.vClient.Get(ctx, client.ObjectKey{Name: vPod.ObjectMeta.GetNamespace()}, vNamespace) + err := t.vClient.Get(ctx, client.ObjectKey{Name: vPod.GetNamespace()}, vNamespace) if err != nil { return err } @@ -31,10 +31,11 @@ func (t *translator) Diff(ctx *synccontext.SyncContext, event *synccontext.SyncE // spec diff t.calcSpecDiff(pPod, vPod) - // check pod and namespace labels - if event.Source == synccontext.SyncEventSourceHost { + switch event.Source { + case synccontext.SyncEventSourceHost: vPod.Labels = translate.VirtualLabels(pPod, vPod) - } else { + vPod.Annotations = translate.VirtualAnnotations(pPod, vPod, GetExcludedAnnotations(pPod)...) + case synccontext.SyncEventSourceVirtual: updatedLabels := translate.HostLabels(vPod, pPod) if updatedLabels == nil { updatedLabels = map[string]string{} @@ -43,31 +44,32 @@ func (t *translator) Diff(ctx *synccontext.SyncContext, event *synccontext.SyncE updatedLabels[translate.HostLabelNamespace(k)] = v } pPod.Labels = updatedLabels - } - // check annotations - updatedAnnotations := translate.HostAnnotations(vPod, pPod, GetExcludedAnnotations(pPod)...) - if updatedAnnotations == nil { - updatedAnnotations = map[string]string{} - } + // check annotations + updatedAnnotations := translate.HostAnnotations(vPod, pPod, GetExcludedAnnotations(pPod)...) + if updatedAnnotations == nil { + updatedAnnotations = map[string]string{} + } - // set owner references - updatedAnnotations[VClusterLabelsAnnotation] = LabelsAnnotation(vPod) - if len(vPod.OwnerReferences) > 0 { - ownerReferencesData, _ := json.Marshal(vPod.OwnerReferences) - updatedAnnotations[OwnerReferences] = string(ownerReferencesData) - for _, ownerReference := range vPod.OwnerReferences { - if ownerReference.APIVersion == appsv1.SchemeGroupVersion.String() && canAnnotateOwnerSetKind(ownerReference.Kind) { - updatedAnnotations[OwnerSetKind] = ownerReference.Kind - break + // set owner references + updatedAnnotations[VClusterLabelsAnnotation] = LabelsAnnotation(vPod) + if len(vPod.OwnerReferences) > 0 { + ownerReferencesData, _ := json.Marshal(vPod.OwnerReferences) + updatedAnnotations[OwnerReferences] = string(ownerReferencesData) + for _, ownerReference := range vPod.OwnerReferences { + if ownerReference.APIVersion == appsv1.SchemeGroupVersion.String() && canAnnotateOwnerSetKind(ownerReference.Kind) { + updatedAnnotations[OwnerSetKind] = ownerReference.Kind + break + } } + } else { + delete(updatedAnnotations, OwnerReferences) + delete(updatedAnnotations, OwnerSetKind) } - } else { - delete(updatedAnnotations, OwnerReferences) - delete(updatedAnnotations, OwnerSetKind) + + pPod.Annotations = updatedAnnotations } - pPod.Annotations = updatedAnnotations return nil } diff --git a/pkg/controllers/resources/runtimeclasses/syncer.go b/pkg/controllers/resources/runtimeclasses/syncer.go index db1c7548f6..b72d93ae5e 100644 --- a/pkg/controllers/resources/runtimeclasses/syncer.go +++ b/pkg/controllers/resources/runtimeclasses/syncer.go @@ -70,8 +70,8 @@ func (i *runtimeClassSyncer) Sync(ctx *synccontext.SyncContext, event *syncconte } }() - event.Virtual.Annotations = event.Host.Annotations - event.Virtual.Labels = event.Host.Labels + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual) + event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) event.Virtual.Handler = event.Host.Handler event.Virtual.Overhead = event.Host.Overhead event.Virtual.Scheduling = event.Host.Scheduling diff --git a/pkg/controllers/resources/secrets/syncer.go b/pkg/controllers/resources/secrets/syncer.go index 87e23bd9d4..fc96afb1d6 100644 --- a/pkg/controllers/resources/secrets/syncer.go +++ b/pkg/controllers/resources/secrets/syncer.go @@ -52,7 +52,7 @@ type secretSyncer struct { var _ syncertypes.Syncer = &secretSyncer{} func (s *secretSyncer) Syncer() syncertypes.Sync[client.Object] { - return syncer.ToGenericSyncer[*corev1.Secret](s) + return syncer.ToGenericSyncer(s) } var _ syncertypes.ControllerModifier = &secretSyncer{} @@ -128,13 +128,11 @@ func (s *secretSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext.Syn event.TargetObject().Type = event.SourceObject().Type } - // check annotations - event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) - - // check labels if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual) event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) } diff --git a/pkg/controllers/resources/serviceaccounts/syncer.go b/pkg/controllers/resources/serviceaccounts/syncer.go index 29867dc8f8..e9f01b8e70 100644 --- a/pkg/controllers/resources/serviceaccounts/syncer.go +++ b/pkg/controllers/resources/serviceaccounts/syncer.go @@ -77,10 +77,11 @@ func (s *serviceAccountSyncer) Sync(ctx *synccontext.SyncContext, event *synccon } }() - event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual) event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) } diff --git a/pkg/controllers/resources/services/syncer.go b/pkg/controllers/resources/services/syncer.go index 4ad987e931..197ea135d7 100644 --- a/pkg/controllers/resources/services/syncer.go +++ b/pkg/controllers/resources/services/syncer.go @@ -133,21 +133,18 @@ func (s *serviceSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext.Sy // update status event.Virtual.Status = event.Host.Status - // check labels if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual, s.excludedAnnotations...) event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host, s.excludedAnnotations...) event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) } - // check annotations - updatedAnnotations := translate.HostAnnotations(event.Virtual, event.Host, s.excludedAnnotations...) - // remove the ServiceBlockDeletion annotation if it's not needed if event.Virtual.Spec.ClusterIP == event.Host.Spec.ClusterIP { - delete(updatedAnnotations, ServiceBlockDeletion) + delete(event.Host.Annotations, ServiceBlockDeletion) } - event.Host.Annotations = updatedAnnotations // translate selector if event.Source == synccontext.SyncEventSourceHost { diff --git a/pkg/controllers/resources/storageclasses/syncer.go b/pkg/controllers/resources/storageclasses/syncer.go index 3d30f7640b..90792d099f 100644 --- a/pkg/controllers/resources/storageclasses/syncer.go +++ b/pkg/controllers/resources/storageclasses/syncer.go @@ -81,8 +81,13 @@ func (s *storageClassSyncer) Sync(ctx *synccontext.SyncContext, event *syncconte } }() - event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host, s.excludedAnnotations...) - event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) + if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual, s.excludedAnnotations...) + event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) + } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host, s.excludedAnnotations...) + event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) + } // bidirectional sync event.TargetObject().Provisioner = event.SourceObject().Provisioner diff --git a/pkg/controllers/resources/volumesnapshotcontents/syncer.go b/pkg/controllers/resources/volumesnapshotcontents/syncer.go index 26f950012c..9b62613478 100644 --- a/pkg/controllers/resources/volumesnapshotcontents/syncer.go +++ b/pkg/controllers/resources/volumesnapshotcontents/syncer.go @@ -188,6 +188,12 @@ func (s *volumeSnapshotContentSyncer) Sync(ctx *synccontext.SyncContext, event * if event.Virtual.Annotations[constants.HostClusterVSCAnnotation] == "" { event.Host.Spec.DeletionPolicy = event.Virtual.Spec.DeletionPolicy event.Host.Spec.VolumeSnapshotClassName = event.Virtual.Spec.VolumeSnapshotClassName + } + + if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual) + event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) + } else { event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) } diff --git a/pkg/controllers/resources/volumesnapshotcontents/syncer_test.go b/pkg/controllers/resources/volumesnapshotcontents/syncer_test.go index b0504de67b..f8d6ec305d 100644 --- a/pkg/controllers/resources/volumesnapshotcontents/syncer_test.go +++ b/pkg/controllers/resources/volumesnapshotcontents/syncer_test.go @@ -88,6 +88,14 @@ func TestSync(t *testing.T) { pDynamicObjectMeta := metav1.ObjectMeta{ Name: "snap-abcd", ResourceVersion: "12345", + Annotations: map[string]string{ + constants.HostClusterVSCAnnotation: "snap-abcd", + translate.ManagedAnnotationsAnnotation: "vcluster.loft.sh/host-volumesnapshotcontent", + translate.HostNameAnnotation: "snap-abcd", + translate.KindAnnotation: "snapshot.storage.k8s.io/v1, Kind=VolumeSnapshotContent", + translate.NameAnnotation: "snap-abcd", + translate.UIDAnnotation: "", + }, } pDynamic := &volumesnapshotv1.VolumeSnapshotContent{ ObjectMeta: pDynamicObjectMeta, @@ -162,10 +170,12 @@ func TestSync(t *testing.T) { vDeletingWithStatus := vDeletingWithOneFinalizer.DeepCopy() vDeletingWithStatus.Status = pDeletingWithStatus.Status - syncertesting.RunTestsWithContext(t, func(vConfig *config.VirtualClusterConfig, pClient *testingutil.FakeIndexClient, vClient *testingutil.FakeIndexClient) *synccontext.RegisterContext { + createContext := func(vConfig *config.VirtualClusterConfig, pClient *testingutil.FakeIndexClient, vClient *testingutil.FakeIndexClient) *synccontext.RegisterContext { vConfig.Sync.ToHost.VolumeSnapshots.Enabled = true return syncertesting.NewFakeRegisterContext(vConfig, pClient, vClient) - }, []*syncertesting.SyncTest{ + } + + testCases := []*syncertesting.SyncTest{ { Name: "Create dynamic VolumeSnapshotContent from host", InitialVirtualState: []runtime.Object{vVolumeSnapshot.DeepCopy()}, @@ -274,7 +284,8 @@ func TestSync(t *testing.T) { volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {}, }, ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{ - volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {}}, + volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {}, + }, Sync: func(ctx *synccontext.RegisterContext) { syncCtx, syncer := newFakeSyncer(t, ctx) _, err := syncer.Sync(syncCtx, synccontext.NewSyncEvent(pPreProvisioned.DeepCopy(), vDeleting.DeepCopy())) @@ -289,7 +300,8 @@ func TestSync(t *testing.T) { volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {}, // fakeClient seems to delete the object that has deletionTimestamp and no finalizers, so we will check its absence }, ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{ - volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {}}, + volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {}, + }, Sync: func(ctx *synccontext.RegisterContext) { syncCtx, syncer := newFakeSyncer(t, ctx) _, err := syncer.SyncToHost(syncCtx, synccontext.NewSyncToHostEvent(vDeletingWithGCFinalizer.DeepCopy())) @@ -304,7 +316,8 @@ func TestSync(t *testing.T) { volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {vDeletingWithOneFinalizer.DeepCopy()}, }, ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{ - volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {pDeletingWithOneFinalizer.DeepCopy()}}, + volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {pDeletingWithOneFinalizer.DeepCopy()}, + }, Sync: func(ctx *synccontext.RegisterContext) { syncCtx, syncer := newFakeSyncer(t, ctx) _, err := syncer.Sync(syncCtx, synccontext.NewSyncEvent(pDeletingWithOneFinalizer.DeepCopy(), vDeletingWithMoreFinalizers.DeepCopy())) @@ -319,12 +332,19 @@ func TestSync(t *testing.T) { volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {vDeletingWithStatus.DeepCopy()}, }, ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{ - volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {pDeletingWithStatus.DeepCopy()}}, + volumesnapshotv1.SchemeGroupVersion.WithKind("VolumeSnapshotContent"): {pDeletingWithStatus.DeepCopy()}, + }, Sync: func(ctx *synccontext.RegisterContext) { syncCtx, syncer := newFakeSyncer(t, ctx) _, err := syncer.Sync(syncCtx, synccontext.NewSyncEvent(pDeletingWithStatus.DeepCopy(), vDeletingWithOneFinalizer.DeepCopy())) assert.NilError(t, err) }, }, - }) + } + + for _, test := range testCases { + t.Run(test.Name, func(t *testing.T) { + test.Run(t, createContext) + }) + } } diff --git a/pkg/controllers/resources/volumesnapshots/syncer.go b/pkg/controllers/resources/volumesnapshots/syncer.go index 42d9ae7c72..ed3c6772bb 100644 --- a/pkg/controllers/resources/volumesnapshots/syncer.go +++ b/pkg/controllers/resources/volumesnapshots/syncer.go @@ -150,8 +150,13 @@ func (s *volumeSnapshotSyncer) Sync(ctx *synccontext.SyncContext, event *synccon event.Host.Spec.VolumeSnapshotClassName = event.Virtual.Spec.VolumeSnapshotClassName // check if metadata changed - event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) - event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) + if event.Source == synccontext.SyncEventSourceHost { + event.Virtual.Annotations = translate.VirtualAnnotations(event.Host, event.Virtual) + event.Virtual.Labels = translate.VirtualLabels(event.Host, event.Virtual) + } else { + event.Host.Annotations = translate.HostAnnotations(event.Virtual, event.Host) + event.Host.Labels = translate.HostLabels(event.Virtual, event.Host) + } return ctrl.Result{}, nil } diff --git a/pkg/syncer/testing/testing.go b/pkg/syncer/testing/testing.go index 2761277c9b..605ff1d8af 100644 --- a/pkg/syncer/testing/testing.go +++ b/pkg/syncer/testing/testing.go @@ -52,7 +52,9 @@ func RunTests(t *testing.T, tests []*SyncTest) { func RunTestsWithContext(t *testing.T, createContext NewContextFunc, tests []*SyncTest) { for _, test := range tests { - test.Run(t, createContext) + t.Run(test.Name, func(t *testing.T) { + test.Run(t, createContext) + }) } } diff --git a/pkg/syncer/wrapper.go b/pkg/syncer/wrapper.go index 12276a90e4..826ed5a672 100644 --- a/pkg/syncer/wrapper.go +++ b/pkg/syncer/wrapper.go @@ -1,10 +1,13 @@ package syncer import ( + "errors" + "github.com/loft-sh/vcluster/pkg/syncer/synccontext" syncertypes "github.com/loft-sh/vcluster/pkg/syncer/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ) func ToGenericSyncer[T client.Object](syncer syncertypes.Sync[T]) syncertypes.Sync[client.Object] { @@ -28,8 +31,11 @@ func (t *toSyncer[T]) SyncToVirtual(ctx *synccontext.SyncContext, event *synccon } func (t *toSyncer[T]) Sync(ctx *synccontext.SyncContext, event *synccontext.SyncEvent[client.Object]) (ctrl.Result, error) { - hostConverted, _ := event.Host.(T) - virtualConverted, _ := event.Virtual.(T) + hostConverted, ok := event.Host.(T) + virtualConverted, ok2 := event.Virtual.(T) + if !ok || !ok2 { + return reconcile.Result{}, errors.New("syncer: type assertion failed") + } return t.syncer.Sync(ctx, &synccontext.SyncEvent[T]{ Type: event.Type, @@ -40,7 +46,10 @@ func (t *toSyncer[T]) Sync(ctx *synccontext.SyncContext, event *synccontext.Sync } func (t *toSyncer[T]) SyncToHost(ctx *synccontext.SyncContext, event *synccontext.SyncToHostEvent[client.Object]) (ctrl.Result, error) { - virtualConverted, _ := event.Virtual.(T) + virtualConverted, ok := event.Virtual.(T) + if !ok { + return reconcile.Result{}, errors.New("syncer: type assertion failed") + } return t.syncer.SyncToHost(ctx, &synccontext.SyncToHostEvent[T]{ Type: event.Type,