diff --git a/pkg/controllers/workapplier/apply.go b/pkg/controllers/workapplier/apply.go index 32f43a0f4..c352c82fd 100644 --- a/pkg/controllers/workapplier/apply.go +++ b/pkg/controllers/workapplier/apply.go @@ -52,7 +52,12 @@ func (r *Reconciler) applyInDryRunMode( ctx context.Context, gvr *schema.GroupVersionResource, manifestObj, inMemberClusterObj *unstructured.Unstructured, + parentCRPName string, ) (*unstructured.Unstructured, error) { + // Add parent-CRP label to namespace objects for namespace affinity support + manifestObjCopy := manifestObj.DeepCopy() + labelNamespaceWithParentCRP(manifestObjCopy, parentCRPName) + // In this method, Fleet will always use forced server-side apply // w/o optimistic lock for diff calculation. // @@ -61,7 +66,7 @@ func (r *Reconciler) applyInDryRunMode( // before the comparison. // // Note that full comparison can be carried out directly without involving the apply op. - return r.serverSideApply(ctx, gvr, manifestObj, inMemberClusterObj, true, false, true) + return r.serverSideApply(ctx, gvr, manifestObjCopy, inMemberClusterObj, true, false, true) } func (r *Reconciler) apply( @@ -70,6 +75,7 @@ func (r *Reconciler) apply( manifestObj, inMemberClusterObj *unstructured.Unstructured, applyStrategy *fleetv1beta1.ApplyStrategy, expectedAppliedWorkOwnerRef *metav1.OwnerReference, + parentCRPName string, ) (*unstructured.Unstructured, error) { // Create a sanitized copy of the manifest object. // @@ -84,6 +90,9 @@ func (r *Reconciler) apply( // backwards compatibility concerns. manifestObjCopy := sanitizeManifestObject(manifestObj) + // Add parent-CRP label to namespace objects for namespace affinity support + labelNamespaceWithParentCRP(manifestObjCopy, parentCRPName) + // Compute the hash of the manifest object. // // Originally the manifest hash is kept only if three-way merge patch (client side apply) @@ -129,7 +138,7 @@ func (r *Reconciler) apply( // Create the object if it does not exist in the member cluster. if inMemberClusterObj == nil { - return r.createManifestObject(ctx, gvr, manifestObjCopy) + return r.createManifestObject(ctx, gvr, manifestObjCopy, parentCRPName) } // Note: originally Fleet will add its owner reference and @@ -199,6 +208,7 @@ func (r *Reconciler) createManifestObject( ctx context.Context, gvr *schema.GroupVersionResource, manifestObject *unstructured.Unstructured, + parentCRPName string, ) (*unstructured.Unstructured, error) { createOpts := metav1.CreateOptions{ FieldManager: workFieldManagerName, @@ -649,3 +659,36 @@ func shouldUseForcedServerSideApply(inMemberClusterObj *unstructured.Unstructure "GVK", inMemberClusterObj.GroupVersionKind(), "inMemberClusterObj", klog.KObj(inMemberClusterObj)) return true } + +// labelNamespaceWithParentCRP adds parent-CRP label to namespace objects being applied. +// This supports the namespace affinity feature where ResourcePlacements can be scheduled +// only on clusters that have the required namespace. +func labelNamespaceWithParentCRP(manifestObj *unstructured.Unstructured, parentCRPName string) { + // Only label if this is a namespace object + if manifestObj.GetKind() != "Namespace" || manifestObj.GetAPIVersion() != "v1" { + return + } + + // Skip if no parent CRP name is provided + if parentCRPName == "" { + klog.Warningf("No parent CRP name provided for namespace %s, skipping labeling", + manifestObj.GetName()) + return + } + + // Get existing labels or create new map + labels := manifestObj.GetLabels() + if labels == nil { + labels = make(map[string]string) + } + + // Add the parent-CRP label + // Format: kubernetes-fleet.io/parent-CRP: {crp-name} + labels[fleetv1beta1.PlacementTrackingLabel] = parentCRPName + + // Set the updated labels back + manifestObj.SetLabels(labels) + + klog.V(2).InfoS("Added parent-CRP label to namespace", + "namespace", manifestObj.GetName(), "crpName", parentCRPName) +} diff --git a/pkg/controllers/workapplier/apply_test.go b/pkg/controllers/workapplier/apply_test.go index 39da8c5d6..4a51058e8 100644 --- a/pkg/controllers/workapplier/apply_test.go +++ b/pkg/controllers/workapplier/apply_test.go @@ -552,3 +552,106 @@ func TestShouldUseForcedServerSideApply(t *testing.T) { }) } } + +// TestLabelNamespaceWithParentCRP tests the labelNamespaceWithParentCRP function. +func TestLabelNamespaceWithParentCRP(t *testing.T) { + testCases := []struct { + name string + manifestObj client.Object + parentCRPName string + wantLabels map[string]string + }{ + { + name: "namespace object with no existing labels", + manifestObj: &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + }, + parentCRPName: "test-crp", + wantLabels: map[string]string{ + fleetv1beta1.PlacementTrackingLabel: "test-crp", + }, + }, + { + name: "namespace object with existing labels", + manifestObj: &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + Labels: map[string]string{ + "existing-label": "existing-value", + }, + }, + }, + parentCRPName: "my-app-crp", + wantLabels: map[string]string{ + "existing-label": "existing-value", + fleetv1beta1.PlacementTrackingLabel: "my-app-crp", + }, + }, + { + name: "non-namespace object should not be modified", + manifestObj: &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-deployment", + Labels: map[string]string{ + "app": "test", + }, + }, + }, + parentCRPName: "test-crp", + wantLabels: map[string]string{ + "app": "test", + }, + }, + { + name: "empty parentCRPName should not add label", + manifestObj: &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + Labels: map[string]string{ + "existing": "label", + }, + }, + }, + parentCRPName: "", + wantLabels: map[string]string{ + "existing": "label", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Convert to unstructured and make a copy to avoid modifying the original test data + manifestCopy := toUnstructured(t, tc.manifestObj).DeepCopy() + + // Call the function under test + labelNamespaceWithParentCRP(manifestCopy, tc.parentCRPName) + + // Get the resulting labels + gotLabels := manifestCopy.GetLabels() + + // Compare the labels + if diff := cmp.Diff(tc.wantLabels, gotLabels); diff != "" { + t.Errorf("labelNamespaceWithParentCRP() labels mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/controllers/workapplier/drift_detection_takeover.go b/pkg/controllers/workapplier/drift_detection_takeover.go index e1461f4b6..335313cd9 100644 --- a/pkg/controllers/workapplier/drift_detection_takeover.go +++ b/pkg/controllers/workapplier/drift_detection_takeover.go @@ -47,6 +47,7 @@ func (r *Reconciler) takeOverPreExistingObject( manifestObj, inMemberClusterObj *unstructured.Unstructured, applyStrategy *fleetv1beta1.ApplyStrategy, expectedAppliedWorkOwnerRef *metav1.OwnerReference, + parentCRPName string, ) (*unstructured.Unstructured, []fleetv1beta1.PatchDetail, bool, error) { inMemberClusterObjCopy := inMemberClusterObj.DeepCopy() existingOwnerRefs := inMemberClusterObjCopy.GetOwnerReferences() @@ -91,7 +92,7 @@ func (r *Reconciler) takeOverPreExistingObject( // // Note that the default takeover action is AlwaysApply. if applyStrategy.WhenToTakeOver == fleetv1beta1.WhenToTakeOverTypeIfNoDiff { - configDiffs, diffCalculatedInDegradedMode, err := r.diffBetweenManifestAndInMemberClusterObjects(ctx, gvr, manifestObj, inMemberClusterObjCopy, applyStrategy.ComparisonOption) + configDiffs, diffCalculatedInDegradedMode, err := r.diffBetweenManifestAndInMemberClusterObjects(ctx, gvr, manifestObj, inMemberClusterObjCopy, applyStrategy.ComparisonOption, parentCRPName) switch { case err != nil: return nil, nil, false, fmt.Errorf("failed to calculate configuration diffs between the manifest object and the object from the member cluster: %w", err) @@ -121,10 +122,11 @@ func (r *Reconciler) diffBetweenManifestAndInMemberClusterObjects( gvr *schema.GroupVersionResource, manifestObj, inMemberClusterObj *unstructured.Unstructured, cmpOption fleetv1beta1.ComparisonOptionType, + parentCRPName string, ) ([]fleetv1beta1.PatchDetail, bool, error) { switch cmpOption { case fleetv1beta1.ComparisonOptionTypePartialComparison: - return r.partialDiffBetweenManifestAndInMemberClusterObjects(ctx, gvr, manifestObj, inMemberClusterObj) + return r.partialDiffBetweenManifestAndInMemberClusterObjects(ctx, gvr, manifestObj, inMemberClusterObj, parentCRPName) case fleetv1beta1.ComparisonOptionTypeFullComparison: // For the full comparison, Fleet compares directly the JSON representations of the // manifest object and the object in the member cluster. @@ -143,10 +145,11 @@ func (r *Reconciler) partialDiffBetweenManifestAndInMemberClusterObjects( ctx context.Context, gvr *schema.GroupVersionResource, manifestObj, inMemberClusterObj *unstructured.Unstructured, + parentCRPName string, ) ([]fleetv1beta1.PatchDetail, bool, error) { // Fleet calculates the partial diff between two objects by running apply ops in the dry-run // mode. - appliedObj, err := r.applyInDryRunMode(ctx, gvr, manifestObj, inMemberClusterObj) + appliedObj, err := r.applyInDryRunMode(ctx, gvr, manifestObj, inMemberClusterObj, parentCRPName) // After the dry-run apply op, all the managed fields should have been overwritten using the // values from the manifest object, while leaving all the unmanaged fields untouched. This diff --git a/pkg/controllers/workapplier/drift_detection_takeover_test.go b/pkg/controllers/workapplier/drift_detection_takeover_test.go index 40c9484e8..825051aff 100644 --- a/pkg/controllers/workapplier/drift_detection_takeover_test.go +++ b/pkg/controllers/workapplier/drift_detection_takeover_test.go @@ -215,7 +215,8 @@ func TestTakeOverPreExistingObject(t *testing.T) { tc.gvr, tc.manifestObj, tc.inMemberClusterObj, tc.applyStrategy, - tc.expectedAppliedWorkOwnerRef) + tc.expectedAppliedWorkOwnerRef, + "test-crp") if tc.wantErred { if err == nil { t.Errorf("takeOverPreExistingObject() = nil, want erred") diff --git a/pkg/controllers/workapplier/process.go b/pkg/controllers/workapplier/process.go index 9072fadfe..2d9c8c119 100644 --- a/pkg/controllers/workapplier/process.go +++ b/pkg/controllers/workapplier/process.go @@ -165,7 +165,10 @@ func (r *Reconciler) processOneManifest( } // Perform the apply op. - appliedObj, err := r.apply(ctx, bundle.gvr, bundle.manifestObj, bundle.inMemberClusterObj, work.Spec.ApplyStrategy, expectedAppliedWorkOwnerRef) + // Extract parent-CRP name from Work object labels for namespace affinity support + parentCRPName := getParentCRPNameFromWork(work) + + appliedObj, err := r.apply(ctx, bundle.gvr, bundle.manifestObj, bundle.inMemberClusterObj, work.Spec.ApplyStrategy, expectedAppliedWorkOwnerRef, parentCRPName) if err != nil { bundle.applyOrReportDiffErr = fmt.Errorf("failed to apply the manifest: %w", err) bundle.applyOrReportDiffResTyp = ApplyOrReportDiffResTypeFailedToApply @@ -256,9 +259,12 @@ func (r *Reconciler) takeOverInMemberClusterObjectIfApplicable( // Take over the object. Note that this steps adds only the owner reference; no other // fields are modified (on the object from the member cluster). + // Extract parent-CRP name for namespace affinity support + parentCRPName := getParentCRPNameFromWork(work) + takenOverInMemberClusterObj, configDiffs, diffCalculatedInDegradedMode, err := r.takeOverPreExistingObject(ctx, bundle.gvr, bundle.manifestObj, bundle.inMemberClusterObj, - work.Spec.ApplyStrategy, expectedAppliedWorkOwnerRef) + work.Spec.ApplyStrategy, expectedAppliedWorkOwnerRef, parentCRPName) switch { case err != nil: // An unexpected error has occurred. @@ -371,10 +377,14 @@ func (r *Reconciler) reportDiffOnlyIfApplicable( // The object has been created in the member cluster; Fleet will calculate the configuration // diffs between the manifest object and the object from the member cluster. + // Extract parent-CRP name for namespace affinity support + parentCRPName := getParentCRPNameFromWork(work) + configDiffs, diffCalculatedInDegradedMode, err := r.diffBetweenManifestAndInMemberClusterObjects(ctx, bundle.gvr, bundle.manifestObj, bundle.inMemberClusterObj, - work.Spec.ApplyStrategy.ComparisonOption) + work.Spec.ApplyStrategy.ComparisonOption, + parentCRPName) switch { case err != nil: // Failed to calculate the configuration diffs. @@ -463,10 +473,14 @@ func (r *Reconciler) performPreApplyDriftDetectionIfApplicable( return false default: // Run the drift detection process. + // Extract parent-CRP name for namespace affinity support + parentCRPName := getParentCRPNameFromWork(work) + drifts, driftsCalculatedInDegradedMode, err := r.diffBetweenManifestAndInMemberClusterObjects(ctx, bundle.gvr, bundle.manifestObj, bundle.inMemberClusterObj, - work.Spec.ApplyStrategy.ComparisonOption) + work.Spec.ApplyStrategy.ComparisonOption, + parentCRPName) switch { case err != nil: // An unexpected error has occurred. @@ -546,10 +560,14 @@ func (r *Reconciler) performPostApplyDriftDetectionIfApplicable( return false } + // Extract parent-CRP name for namespace affinity support + parentCRPName := getParentCRPNameFromWork(work) + drifts, driftsCalculatedInDegradedMode, err := r.diffBetweenManifestAndInMemberClusterObjects(ctx, bundle.gvr, bundle.manifestObj, bundle.inMemberClusterObj, - work.Spec.ApplyStrategy.ComparisonOption) + work.Spec.ApplyStrategy.ComparisonOption, + parentCRPName) switch { case err != nil: // An unexpected error has occurred. @@ -595,3 +613,12 @@ func shouldPerformPostApplyDriftDetection(applyStrategy *fleetv1beta1.ApplyStrat // * The apply strategy dictates that drift detection should run in full comparison mode. return applyStrategy.ComparisonOption == fleetv1beta1.ComparisonOptionTypeFullComparison } + +// getParentCRPNameFromWork extracts the parent CRP name from Work object labels. +// Returns empty string if the label is not found. +func getParentCRPNameFromWork(work *fleetv1beta1.Work) string { + if workLabels := work.GetLabels(); workLabels != nil { + return workLabels[fleetv1beta1.PlacementTrackingLabel] + } + return "" +} diff --git a/pkg/controllers/workapplier/process_test.go b/pkg/controllers/workapplier/process_test.go index e3ab39ef1..34ae82b67 100644 --- a/pkg/controllers/workapplier/process_test.go +++ b/pkg/controllers/workapplier/process_test.go @@ -97,3 +97,97 @@ func TestShouldInitiateTakeOverAttempt(t *testing.T) { }) } } + +// TestGetParentCRPNameFromWork tests the getParentCRPNameFromWork function. +func TestGetParentCRPNameFromWork(t *testing.T) { + testCases := []struct { + name string + work *fleetv1beta1.Work + want string + }{ + { + name: "work with parent-CRP label", + work: &fleetv1beta1.Work{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-work", + Namespace: "fleet-member-cluster-1", + Labels: map[string]string{ + fleetv1beta1.PlacementTrackingLabel: "test-crp", + "other-label": "other-value", + }, + }, + }, + want: "test-crp", + }, + { + name: "work without parent-CRP label", + work: &fleetv1beta1.Work{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-work", + Namespace: "fleet-member-cluster-1", + Labels: map[string]string{ + "other-label": "other-value", + }, + }, + }, + want: "", + }, + { + name: "work with no labels", + work: &fleetv1beta1.Work{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-work", + Namespace: "fleet-member-cluster-1", + }, + }, + want: "", + }, + { + name: "work with nil labels map", + work: &fleetv1beta1.Work{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-work", + Namespace: "fleet-member-cluster-1", + Labels: nil, + }, + }, + want: "", + }, + { + name: "work with empty parent-CRP label value", + work: &fleetv1beta1.Work{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-work", + Namespace: "fleet-member-cluster-1", + Labels: map[string]string{ + fleetv1beta1.PlacementTrackingLabel: "", + "other-label": "other-value", + }, + }, + }, + want: "", + }, + { + name: "work with complex CRP name", + work: &fleetv1beta1.Work{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-work", + Namespace: "fleet-member-cluster-1", + Labels: map[string]string{ + fleetv1beta1.PlacementTrackingLabel: "my-app-production-crp", + }, + }, + }, + want: "my-app-production-crp", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := getParentCRPNameFromWork(tc.work) + if got != tc.want { + t.Errorf("getParentCRPNameFromWork() = %v, want %v", got, tc.want) + } + }) + } +} diff --git a/test/e2e/actuals_test.go b/test/e2e/actuals_test.go index 8bbc0dcbe..ef23f30ca 100644 --- a/test/e2e/actuals_test.go +++ b/test/e2e/actuals_test.go @@ -56,6 +56,7 @@ func validateWorkNamespaceOnCluster(cluster *framework.Cluster, name types.Names ignoreNamespaceStatusField, ignoreObjectMetaAutoGeneratedFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ); diff != "" { return fmt.Errorf("work namespace diff (-got, +want): %s", diff) } diff --git a/test/e2e/placement_apply_strategy_test.go b/test/e2e/placement_apply_strategy_test.go index 2674ef3a0..67abc5f1b 100644 --- a/test/e2e/placement_apply_strategy_test.go +++ b/test/e2e/placement_apply_strategy_test.go @@ -581,6 +581,7 @@ var _ = Describe("validating CRP when resources exists", Ordered, func() { ignoreNamespaceStatusField, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "Namespace diff (-got +want):\n%s", diff) @@ -597,6 +598,7 @@ var _ = Describe("validating CRP when resources exists", Ordered, func() { cm, &wantCM, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "ConfigMap diff (-got +want):\n%s", diff) }) diff --git a/test/e2e/placement_drift_diff_test.go b/test/e2e/placement_drift_diff_test.go index 0e5a7a495..10bc48198 100644 --- a/test/e2e/placement_drift_diff_test.go +++ b/test/e2e/placement_drift_diff_test.go @@ -104,6 +104,7 @@ var _ = Describe("take over existing resources", func() { ignoreNamespaceStatusField, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "Namespace diff (-got +want):\n%s", diff) @@ -120,6 +121,7 @@ var _ = Describe("take over existing resources", func() { cm, &wantCM, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "ConfigMap diff (-got +want):\n%s", diff) }) @@ -272,6 +274,7 @@ var _ = Describe("take over existing resources", func() { ignoreNamespaceStatusField, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "Namespace diff (-got +want):\n%s", diff) }) @@ -292,6 +295,7 @@ var _ = Describe("take over existing resources", func() { cm, &wantCM, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "ConfigMap diff (-got +want):\n%s", diff) }) @@ -468,6 +472,7 @@ var _ = Describe("take over existing resources", func() { ignoreNamespaceStatusField, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "Namespace diff (-got +want):\n%s", diff) @@ -486,6 +491,7 @@ var _ = Describe("take over existing resources", func() { cm, &wantCM, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "ConfigMap diff (-got +want):\n%s", diff) }) @@ -634,6 +640,7 @@ var _ = Describe("detect drifts on placed resources", func() { cm, &wantCM, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "ConfigMap diff (-got +want):\n%s", diff) }) @@ -797,6 +804,7 @@ var _ = Describe("detect drifts on placed resources", func() { ignoreNamespaceStatusField, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "Namespace diff (-got +want):\n%s", diff) @@ -818,6 +826,7 @@ var _ = Describe("detect drifts on placed resources", func() { cm, &wantCM, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "ConfigMap diff (-got +want):\n%s", diff) }) @@ -1008,6 +1017,7 @@ var _ = Describe("detect drifts on placed resources", func() { ignoreNamespaceStatusField, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "Namespace diff (-got +want):\n%s", diff) @@ -1029,6 +1039,7 @@ var _ = Describe("detect drifts on placed resources", func() { cm, &wantCM, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "ConfigMap diff (-got +want):\n%s", diff) }) @@ -1238,6 +1249,7 @@ var _ = Describe("report diff mode", func() { ignoreNamespaceStatusField, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "Namespace diff (-got +want):\n%s", diff) @@ -1256,6 +1268,7 @@ var _ = Describe("report diff mode", func() { cm, &wantCM, ignoreObjectMetaAutoGenExceptOwnerRefFields, ignoreObjectMetaAnnotationField, + ignoreParentCRPLabel, ) Expect(diff).To(BeEmpty(), "ConfigMap diff (-got +want):\n%s", diff) }) diff --git a/test/e2e/setup_test.go b/test/e2e/setup_test.go index 62b5d54ab..a85d1e1d6 100644 --- a/test/e2e/setup_test.go +++ b/test/e2e/setup_test.go @@ -205,6 +205,7 @@ var ( ignoreObjectMetaAutoGeneratedFields = cmpopts.IgnoreFields(metav1.ObjectMeta{}, "UID", "CreationTimestamp", "ResourceVersion", "Generation", "ManagedFields", "OwnerReferences") ignoreObjectMetaAutoGenExceptOwnerRefFields = cmpopts.IgnoreFields(metav1.ObjectMeta{}, "UID", "CreationTimestamp", "ResourceVersion", "Generation", "ManagedFields") ignoreObjectMetaAnnotationField = cmpopts.IgnoreFields(metav1.ObjectMeta{}, "Annotations") + ignoreParentCRPLabel = cmpopts.IgnoreMapEntries(func(key string, _ string) bool { return key == placementv1beta1.PlacementTrackingLabel }) ignoreConditionObservedGenerationField = cmpopts.IgnoreFields(metav1.Condition{}, "ObservedGeneration") ignoreConditionReasonField = cmpopts.IgnoreFields(metav1.Condition{}, "Reason")