diff --git a/pkg/detector/compare.go b/pkg/detector/compare.go index 55be28942..56ad592dc 100644 --- a/pkg/detector/compare.go +++ b/pkg/detector/compare.go @@ -1,6 +1,8 @@ package detector import ( + "math" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/klog/v2" @@ -9,15 +11,32 @@ import ( "github.com/karmada-io/karmada/pkg/util/fedinformer/keys" ) -func getHighestPriorityPropagationPolicies(policies []*policyv1alpha1.PropagationPolicy, resource *unstructured.Unstructured, objectKey keys.ClusterWideKey) *policyv1alpha1.PropagationPolicy { +func getHighestPriorityPropagationPolicy(policies []*policyv1alpha1.PropagationPolicy, resource *unstructured.Unstructured, objectKey keys.ClusterWideKey) *policyv1alpha1.PropagationPolicy { + matchedPolicyImplicitPriority := util.PriorityMisMatch + matchedPolicyExplicitPriority := int32(math.MinInt32) var matchedPolicy *policyv1alpha1.PropagationPolicy - matchedPolicyPriority := util.PriorityMisMatch + for _, policy := range policies { - if p := util.ResourceMatchSelectorsPriority(resource, policy.Spec.ResourceSelectors...); p > matchedPolicyPriority { + implicitPriority := util.ResourceMatchSelectorsPriority(resource, policy.Spec.ResourceSelectors...) + if implicitPriority <= util.PriorityMisMatch { + continue + } + explicitPriority := policy.ExplicitPriority() + + if matchedPolicyExplicitPriority < explicitPriority { + matchedPolicyImplicitPriority = implicitPriority + matchedPolicyExplicitPriority = explicitPriority matchedPolicy = policy - matchedPolicyPriority = p - } else if p > util.PriorityMisMatch && p == matchedPolicyPriority { - matchedPolicy = getHigherPriorityPropagationPolicy(matchedPolicy, policy) + continue + } + + if matchedPolicyExplicitPriority == explicitPriority { + if implicitPriority > matchedPolicyImplicitPriority { + matchedPolicyImplicitPriority = implicitPriority + matchedPolicy = policy + } else if implicitPriority == matchedPolicyImplicitPriority { + matchedPolicy = getHigherPriorityPropagationPolicy(matchedPolicy, policy) + } } } @@ -29,15 +48,32 @@ func getHighestPriorityPropagationPolicies(policies []*policyv1alpha1.Propagatio return matchedPolicy } -func getHighestPriorityClusterPropagationPolicies(policies []*policyv1alpha1.ClusterPropagationPolicy, resource *unstructured.Unstructured, objectKey keys.ClusterWideKey) *policyv1alpha1.ClusterPropagationPolicy { +func getHighestPriorityClusterPropagationPolicy(policies []*policyv1alpha1.ClusterPropagationPolicy, resource *unstructured.Unstructured, objectKey keys.ClusterWideKey) *policyv1alpha1.ClusterPropagationPolicy { + matchedClusterPolicyImplicitPriority := util.PriorityMisMatch + matchedClusterPolicyExplicitPriority := int32(math.MinInt32) var matchedClusterPolicy *policyv1alpha1.ClusterPropagationPolicy - matchedClusterPolicyPriority := util.PriorityMisMatch + for _, policy := range policies { - if p := util.ResourceMatchSelectorsPriority(resource, policy.Spec.ResourceSelectors...); p > matchedClusterPolicyPriority { + implicitPriority := util.ResourceMatchSelectorsPriority(resource, policy.Spec.ResourceSelectors...) + if implicitPriority <= util.PriorityMisMatch { + continue + } + explicitPriority := policy.ExplicitPriority() + + if matchedClusterPolicyExplicitPriority < explicitPriority { + matchedClusterPolicyImplicitPriority = implicitPriority + matchedClusterPolicyExplicitPriority = explicitPriority matchedClusterPolicy = policy - matchedClusterPolicyPriority = p - } else if p > util.PriorityMisMatch && p == matchedClusterPolicyPriority { - matchedClusterPolicy = getHigherPriorityClusterPropagationPolicy(matchedClusterPolicy, policy) + continue + } + + if matchedClusterPolicyExplicitPriority == explicitPriority { + if implicitPriority > matchedClusterPolicyImplicitPriority { + matchedClusterPolicyImplicitPriority = implicitPriority + matchedClusterPolicy = policy + } else if implicitPriority == matchedClusterPolicyImplicitPriority { + matchedClusterPolicy = getHigherPriorityClusterPropagationPolicy(matchedClusterPolicy, policy) + } } } diff --git a/pkg/detector/compare_test.go b/pkg/detector/compare_test.go index c8cea095b..7887475c7 100644 --- a/pkg/detector/compare_test.go +++ b/pkg/detector/compare_test.go @@ -260,7 +260,7 @@ func Test_getHighestPriorityPropagationPolicies(t *testing.T) { want: nil, }, { - name: "different priority policy", + name: "different implicit priority policy", args: args{ policies: []*policyv1alpha1.PropagationPolicy{ { @@ -321,7 +321,7 @@ func Test_getHighestPriorityPropagationPolicies(t *testing.T) { }, }, { - name: "same priority policy", + name: "same implicit priority policy", args: args{ policies: []*policyv1alpha1.PropagationPolicy{ { @@ -371,11 +371,314 @@ func Test_getHighestPriorityPropagationPolicies(t *testing.T) { }}}, }, }, + { + name: "one policy with implicit priority, one policy with explicit priority 1", + args: args{ + policies: []*policyv1alpha1.PropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.PropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + }, + { + name: "one policy with explicit priority 1(name match), one policy with explicit priority 2(label selector match)", + args: args{ + policies: []*policyv1alpha1.PropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(2) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.PropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(2) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + }, + { + name: "two policies with explicit priority 1(name match), select the one with lower alphabetical order", + args: args{ + policies: []*policyv1alpha1.PropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.PropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + { + name: "one policy with explicit priority 1(name match), one policy with explicit priority 1(label selector match)", + args: args{ + policies: []*policyv1alpha1.PropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.PropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + { + name: "one policy with explicit priority -1(name match), one policy with implicit priority(label selector match)", + args: args{ + policies: []*policyv1alpha1.PropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(-1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.PropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := getHighestPriorityPropagationPolicies(tt.args.policies, tt.args.resource, tt.args.objectKey); !reflect.DeepEqual(got, tt.want) { - t.Errorf("getHighestPriorityPropagationPolicies() = %v, want %v", got, tt.want) + if got := getHighestPriorityPropagationPolicy(tt.args.policies, tt.args.resource, tt.args.objectKey); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getHighestPriorityPropagationPolicy() = %v, want %v", got, tt.want) } }) } @@ -452,7 +755,7 @@ func Test_getHighestPriorityClusterPropagationPolicies(t *testing.T) { want: nil, }, { - name: "different priority policy", + name: "different implicit priority policy", args: args{ policies: []*policyv1alpha1.ClusterPropagationPolicy{ { @@ -513,7 +816,7 @@ func Test_getHighestPriorityClusterPropagationPolicies(t *testing.T) { }, }, { - name: "same priority policy", + name: "same implicit priority policy", args: args{ policies: []*policyv1alpha1.ClusterPropagationPolicy{ { @@ -563,11 +866,314 @@ func Test_getHighestPriorityClusterPropagationPolicies(t *testing.T) { }}}, }, }, + { + name: "one policy with implicit priority, one policy with explicit priority 1", + args: args{ + policies: []*policyv1alpha1.ClusterPropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.ClusterPropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + }, + { + name: "one policy with explicit priority 1(name match), one policy with explicit priority 2(label selector match)", + args: args{ + policies: []*policyv1alpha1.ClusterPropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(2) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.ClusterPropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(2) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + }, + { + name: "two policies with explicit priority 1(name match), select the one with lower alphabetical order", + args: args{ + policies: []*policyv1alpha1.ClusterPropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.ClusterPropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + { + name: "one policy with explicit priority 1(name match), one policy with explicit priority 1(label selector match)", + args: args{ + policies: []*policyv1alpha1.ClusterPropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.ClusterPropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + { + name: "one policy with explicit priority -1(name match), one policy with implicit priority(label selector match)", + args: args{ + policies: []*policyv1alpha1.ClusterPropagationPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "b-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + Priority: func() *int32 { + p := int32(-1) + return &p + }(), + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + Name: "nginx", + }}}, + }, + }, + resource: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "nginx", + "namespace": "test", + "labels": map[string]interface{}{ + "app": "nginx", + }}}}, + objectKey: keys.ClusterWideKey{Kind: "Deployment", Namespace: "test", Name: "nginx"}, + }, + want: &policyv1alpha1.ClusterPropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "a-pp", Namespace: "test"}, + Spec: policyv1alpha1.PropagationSpec{ + ResourceSelectors: []policyv1alpha1.ResourceSelector{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Namespace: "test", + LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}}, + }}}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := getHighestPriorityClusterPropagationPolicies(tt.args.policies, tt.args.resource, tt.args.objectKey); !reflect.DeepEqual(got, tt.want) { - t.Errorf("getHighestPriorityClusterPropagationPolicies() = %v, want %v", got, tt.want) + if got := getHighestPriorityClusterPropagationPolicy(tt.args.policies, tt.args.resource, tt.args.objectKey); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getHighestPriorityPropagationPolicies() = %v, want %v", got, tt.want) } }) } diff --git a/pkg/detector/detector.go b/pkg/detector/detector.go index 6603ab3b4..603e6d435 100644 --- a/pkg/detector/detector.go +++ b/pkg/detector/detector.go @@ -337,7 +337,7 @@ func (d *ResourceDetector) LookForMatchedPolicy(object *unstructured.Unstructure policyList = append(policyList, policy) } - return getHighestPriorityPropagationPolicies(policyList, object, objectKey), nil + return getHighestPriorityPropagationPolicy(policyList, object, objectKey), nil } // LookForMatchedClusterPolicy tries to find a ClusterPropagationPolicy for object referenced by object key. @@ -363,7 +363,7 @@ func (d *ResourceDetector) LookForMatchedClusterPolicy(object *unstructured.Unst policyList = append(policyList, policy) } - return getHighestPriorityClusterPropagationPolicies(policyList, object, objectKey), nil + return getHighestPriorityClusterPropagationPolicy(policyList, object, objectKey), nil } // ApplyPolicy starts propagate the object referenced by object key according to PropagationPolicy. diff --git a/test/e2e/clusterpropagationpolicy_test.go b/test/e2e/clusterpropagationpolicy_test.go index 3af8a9050..4e14b01ee 100644 --- a/test/e2e/clusterpropagationpolicy_test.go +++ b/test/e2e/clusterpropagationpolicy_test.go @@ -7,6 +7,8 @@ import ( appsv1 "k8s.io/api/apps/v1" rbacv1 "k8s.io/api/rbac/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/rand" policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" @@ -318,3 +320,148 @@ var _ = ginkgo.Describe("[AdvancedClusterPropagation] propagation testing", func }) }) }) + +// ExplicitPriority more than one CPP matches the object, we should select the one with the highest explicit priority, if the +// explicit priority is same, select the one with the highest implicit priority. +var _ = ginkgo.Describe("[ExplicitPriority] propagation testing", func() { + ginkgo.Context("high explicit/low priority/implicit priority ClusterPropagationPolicy propagation testing", func() { + var higherPriorityLabelSelector, lowerPriorityMatchName, implicitPriorityMatchName string + var deploymentNamespace, deploymentName string + var deployment *appsv1.Deployment + var policyHigherPriorityLabelSelector, policyLowerPriorityMatchName, policyImplicitPriorityMatchName *policyv1alpha1.ClusterPropagationPolicy + var priorityLabelKey = "priority" + var priorityLabelValue = "priority" + + ginkgo.BeforeEach(func() { + higherPriorityLabelSelector = deploymentNamePrefix + rand.String(RandomStrLength) + lowerPriorityMatchName = deploymentNamePrefix + rand.String(RandomStrLength) + implicitPriorityMatchName = deploymentNamePrefix + rand.String(RandomStrLength) + deploymentNamespace = testNamespace + deploymentName = deploymentNamePrefix + rand.String(RandomStrLength) + + deployment = testhelper.NewDeployment(deploymentNamespace, deploymentName) + deployment.SetLabels(map[string]string{priorityLabelKey: priorityLabelValue}) + policyHigherPriorityLabelSelector = testhelper.NewExplicitPriorityClusterPropagationPolicy(higherPriorityLabelSelector, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + LabelSelector: metav1.SetAsLabelSelector(labels.Set{priorityLabelKey: priorityLabelValue}), + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }, 2) + policyLowerPriorityMatchName = testhelper.NewExplicitPriorityClusterPropagationPolicy(lowerPriorityMatchName, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + Name: deployment.Name, + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }, 1) + policyImplicitPriorityMatchName = testhelper.NewClusterPropagationPolicy(implicitPriorityMatchName, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + Name: deployment.Name, + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }) + }) + + ginkgo.BeforeEach(func() { + framework.CreateClusterPropagationPolicy(karmadaClient, policyHigherPriorityLabelSelector) + framework.CreateClusterPropagationPolicy(karmadaClient, policyLowerPriorityMatchName) + framework.CreateClusterPropagationPolicy(karmadaClient, policyImplicitPriorityMatchName) + framework.CreateDeployment(kubeClient, deployment) + ginkgo.DeferCleanup(func() { + framework.RemoveDeployment(kubeClient, deployment.Namespace, deployment.Name) + framework.WaitDeploymentDisappearOnClusters(framework.ClusterNames(), deployment.Namespace, deployment.Name) + }) + ginkgo.DeferCleanup(func() { + framework.RemoveClusterPropagationPolicy(karmadaClient, policyHigherPriorityLabelSelector.Name) + framework.RemoveClusterPropagationPolicy(karmadaClient, policyLowerPriorityMatchName.Name) + }) + }) + + ginkgo.It("high explicit/low priority/implicit priority ClusterPropagationPolicy testing", func() { + ginkgo.By("check whether the deployment uses the highest explicit priority ClusterPropagationPolicy", func() { + framework.WaitDeploymentPresentOnClustersFitWith(framework.ClusterNames(), deployment.Namespace, deployment.Name, + func(deployment *appsv1.Deployment) bool { + return deployment.GetLabels()[policyv1alpha1.ClusterPropagationPolicyLabel] == higherPriorityLabelSelector + }) + }) + }) + }) + + ginkgo.Context("same explicit priority ClusterPropagationPolicy propagation testing", func() { + var explicitPriorityLabelSelector, explicitPriorityMatchName string + var deploymentNamespace, deploymentName string + var deployment *appsv1.Deployment + var policyExplicitPriorityLabelSelector, policyExplicitPriorityMatchName *policyv1alpha1.ClusterPropagationPolicy + var priorityLabelKey = "priority" + var priorityLabelValue = "priority" + + ginkgo.BeforeEach(func() { + explicitPriorityLabelSelector = deploymentNamePrefix + rand.String(RandomStrLength) + explicitPriorityMatchName = deploymentNamePrefix + rand.String(RandomStrLength) + deploymentNamespace = testNamespace + deploymentName = deploymentNamePrefix + rand.String(RandomStrLength) + + deployment = testhelper.NewDeployment(deploymentNamespace, deploymentName) + deployment.SetLabels(map[string]string{priorityLabelKey: priorityLabelValue}) + policyExplicitPriorityLabelSelector = testhelper.NewExplicitPriorityClusterPropagationPolicy(explicitPriorityLabelSelector, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + LabelSelector: metav1.SetAsLabelSelector(labels.Set{priorityLabelKey: priorityLabelValue}), + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }, 1) + policyExplicitPriorityMatchName = testhelper.NewExplicitPriorityClusterPropagationPolicy(explicitPriorityMatchName, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + Name: deployment.Name, + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }, 1) + }) + + ginkgo.BeforeEach(func() { + framework.CreateClusterPropagationPolicy(karmadaClient, policyExplicitPriorityLabelSelector) + framework.CreateClusterPropagationPolicy(karmadaClient, policyExplicitPriorityMatchName) + framework.CreateDeployment(kubeClient, deployment) + ginkgo.DeferCleanup(func() { + framework.RemoveDeployment(kubeClient, deployment.Namespace, deployment.Name) + framework.WaitDeploymentDisappearOnClusters(framework.ClusterNames(), deployment.Namespace, deployment.Name) + }) + ginkgo.DeferCleanup(func() { + framework.RemoveClusterPropagationPolicy(karmadaClient, policyExplicitPriorityLabelSelector.Name) + framework.RemoveClusterPropagationPolicy(karmadaClient, policyExplicitPriorityMatchName.Name) + }) + }) + + ginkgo.It("same explicit priority ClusterPropagationPolicy propagation testing", func() { + ginkgo.By("check whether the deployment uses the ClusterPropagationPolicy with name matched", func() { + framework.WaitDeploymentPresentOnClustersFitWith(framework.ClusterNames(), deployment.Namespace, deployment.Name, + func(deployment *appsv1.Deployment) bool { + return deployment.GetLabels()[policyv1alpha1.ClusterPropagationPolicyLabel] == explicitPriorityMatchName + }) + }) + }) + }) +}) diff --git a/test/e2e/propagationpolicy_test.go b/test/e2e/propagationpolicy_test.go index a72b29b73..92fd99903 100644 --- a/test/e2e/propagationpolicy_test.go +++ b/test/e2e/propagationpolicy_test.go @@ -691,6 +691,153 @@ var _ = ginkgo.Describe("[ImplicitPriority] propagation testing", func() { }) }) +// ExplicitPriority more than one PP matches the object, we should select the one with the highest explicit priority, if the +// explicit priority is same, select the one with the highest implicit priority. +var _ = ginkgo.Describe("[ExplicitPriority] propagation testing", func() { + ginkgo.Context("high explicit/low priority/implicit priority PropagationPolicy propagation testing", func() { + var policyNamespace, higherPriorityLabelSelector, lowerPriorityMatchName, implicitPriorityMatchName string + var deploymentNamespace, deploymentName string + var deployment *appsv1.Deployment + var policyHigherPriorityLabelSelector, policyLowerPriorityMatchMatchName, policyImplicitPriorityMatchMatchName *policyv1alpha1.PropagationPolicy + var priorityLabelKey = "priority" + var priorityLabelValue = "priority" + + ginkgo.BeforeEach(func() { + policyNamespace = testNamespace + higherPriorityLabelSelector = deploymentNamePrefix + rand.String(RandomStrLength) + lowerPriorityMatchName = deploymentNamePrefix + rand.String(RandomStrLength) + implicitPriorityMatchName = deploymentNamePrefix + rand.String(RandomStrLength) + deploymentNamespace = testNamespace + deploymentName = deploymentNamePrefix + rand.String(RandomStrLength) + + deployment = testhelper.NewDeployment(deploymentNamespace, deploymentName) + deployment.SetLabels(map[string]string{priorityLabelKey: priorityLabelValue}) + policyHigherPriorityLabelSelector = testhelper.NewExplicitPriorityPropagationPolicy(policyNamespace, higherPriorityLabelSelector, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + LabelSelector: metav1.SetAsLabelSelector(labels.Set{priorityLabelKey: priorityLabelValue}), + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }, 2) + policyLowerPriorityMatchMatchName = testhelper.NewExplicitPriorityPropagationPolicy(policyNamespace, lowerPriorityMatchName, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + Name: deployment.Name, + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }, 1) + policyImplicitPriorityMatchMatchName = testhelper.NewPropagationPolicy(policyNamespace, implicitPriorityMatchName, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + Name: deployment.Name, + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }) + }) + + ginkgo.BeforeEach(func() { + framework.CreatePropagationPolicy(karmadaClient, policyHigherPriorityLabelSelector) + framework.CreatePropagationPolicy(karmadaClient, policyLowerPriorityMatchMatchName) + framework.CreatePropagationPolicy(karmadaClient, policyImplicitPriorityMatchMatchName) + framework.CreateDeployment(kubeClient, deployment) + ginkgo.DeferCleanup(func() { + framework.RemoveDeployment(kubeClient, deployment.Namespace, deployment.Name) + framework.WaitDeploymentDisappearOnClusters(framework.ClusterNames(), deployment.Namespace, deployment.Name) + }) + ginkgo.DeferCleanup(func() { + framework.RemovePropagationPolicy(karmadaClient, policyHigherPriorityLabelSelector.Namespace, policyHigherPriorityLabelSelector.Name) + framework.RemovePropagationPolicy(karmadaClient, policyLowerPriorityMatchMatchName.Namespace, policyLowerPriorityMatchMatchName.Name) + }) + }) + + ginkgo.It("high explicit/low priority/implicit priority PropagationPolicy propagation testing", func() { + ginkgo.By("check whether the deployment uses the highest explicit priority PropagationPolicy", func() { + framework.WaitDeploymentPresentOnClustersFitWith(framework.ClusterNames(), deployment.Namespace, deployment.Name, + func(deployment *appsv1.Deployment) bool { + return deployment.GetLabels()[policyv1alpha1.PropagationPolicyNameLabel] == higherPriorityLabelSelector + }) + }) + }) + }) + + ginkgo.Context("same explicit priority PropagationPolicy propagation testing", func() { + var policyNamespace, explicitPriorityLabelSelector, explicitPriorityMatchName string + var deploymentNamespace, deploymentName string + var deployment *appsv1.Deployment + var policyExplicitPriorityLabelSelector, policyExplicitPriorityMatchName *policyv1alpha1.PropagationPolicy + var priorityLabelKey = "priority" + var priorityLabelValue = "priority" + + ginkgo.BeforeEach(func() { + policyNamespace = testNamespace + explicitPriorityLabelSelector = deploymentNamePrefix + rand.String(RandomStrLength) + explicitPriorityMatchName = deploymentNamePrefix + rand.String(RandomStrLength) + deploymentNamespace = testNamespace + deploymentName = deploymentNamePrefix + rand.String(RandomStrLength) + + deployment = testhelper.NewDeployment(deploymentNamespace, deploymentName) + deployment.SetLabels(map[string]string{priorityLabelKey: priorityLabelValue}) + policyExplicitPriorityLabelSelector = testhelper.NewExplicitPriorityPropagationPolicy(policyNamespace, explicitPriorityLabelSelector, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + LabelSelector: metav1.SetAsLabelSelector(labels.Set{priorityLabelKey: priorityLabelValue}), + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }, 1) + policyExplicitPriorityMatchName = testhelper.NewExplicitPriorityPropagationPolicy(policyNamespace, explicitPriorityMatchName, []policyv1alpha1.ResourceSelector{ + { + APIVersion: deployment.APIVersion, + Kind: deployment.Kind, + Name: deployment.Name, + }, + }, policyv1alpha1.Placement{ + ClusterAffinity: &policyv1alpha1.ClusterAffinity{ + ClusterNames: framework.ClusterNames(), + }, + }, 1) + }) + + ginkgo.BeforeEach(func() { + framework.CreatePropagationPolicy(karmadaClient, policyExplicitPriorityLabelSelector) + framework.CreatePropagationPolicy(karmadaClient, policyExplicitPriorityMatchName) + framework.CreateDeployment(kubeClient, deployment) + ginkgo.DeferCleanup(func() { + framework.RemoveDeployment(kubeClient, deployment.Namespace, deployment.Name) + framework.WaitDeploymentDisappearOnClusters(framework.ClusterNames(), deployment.Namespace, deployment.Name) + }) + ginkgo.DeferCleanup(func() { + framework.RemovePropagationPolicy(karmadaClient, policyExplicitPriorityLabelSelector.Namespace, policyExplicitPriorityLabelSelector.Name) + framework.RemovePropagationPolicy(karmadaClient, policyExplicitPriorityMatchName.Namespace, policyExplicitPriorityMatchName.Name) + }) + }) + + ginkgo.It("same explicit priority PropagationPolicy propagation testing", func() { + ginkgo.By("check whether the deployment uses the PropagationPolicy with name matched", func() { + framework.WaitDeploymentPresentOnClustersFitWith(framework.ClusterNames(), deployment.Namespace, deployment.Name, + func(deployment *appsv1.Deployment) bool { + return deployment.GetLabels()[policyv1alpha1.PropagationPolicyNameLabel] == explicitPriorityMatchName + }) + }) + }) + }) +}) + // AdvancedPropagation focus on some advanced propagation testing. var _ = ginkgo.Describe("[AdvancedPropagation] propagation testing", func() { ginkgo.Context("Edit PropagationPolicy ResourceSelectors", func() { diff --git a/test/helper/policy.go b/test/helper/policy.go index 291162074..f97beefe5 100644 --- a/test/helper/policy.go +++ b/test/helper/policy.go @@ -22,6 +22,22 @@ func NewPropagationPolicy(ns, name string, rsSelectors []policyv1alpha1.Resource } } +// NewExplicitPriorityPropagationPolicy will build a PropagationPolicy object with explicit priority. +func NewExplicitPriorityPropagationPolicy(ns, name string, rsSelectors []policyv1alpha1.ResourceSelector, + placement policyv1alpha1.Placement, priority int32) *policyv1alpha1.PropagationPolicy { + return &policyv1alpha1.PropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + Spec: policyv1alpha1.PropagationSpec{ + ResourceSelectors: rsSelectors, + Priority: &priority, + Placement: placement, + }, + } +} + // NewClusterPropagationPolicy will build a ClusterPropagationPolicy object. func NewClusterPropagationPolicy(policyName string, rsSelectors []policyv1alpha1.ResourceSelector, placement policyv1alpha1.Placement) *policyv1alpha1.ClusterPropagationPolicy { return &policyv1alpha1.ClusterPropagationPolicy{ @@ -35,6 +51,21 @@ func NewClusterPropagationPolicy(policyName string, rsSelectors []policyv1alpha1 } } +// NewExplicitPriorityClusterPropagationPolicy will build a ClusterPropagationPolicy object with explicit priority. +func NewExplicitPriorityClusterPropagationPolicy(policyName string, rsSelectors []policyv1alpha1.ResourceSelector, + placement policyv1alpha1.Placement, priority int32) *policyv1alpha1.ClusterPropagationPolicy { + return &policyv1alpha1.ClusterPropagationPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: policyName, + }, + Spec: policyv1alpha1.PropagationSpec{ + ResourceSelectors: rsSelectors, + Priority: &priority, + Placement: placement, + }, + } +} + // NewOverridePolicy will build a OverridePolicy object. func NewOverridePolicy(namespace, policyName string, rsSelectors []policyv1alpha1.ResourceSelector, clusterAffinity policyv1alpha1.ClusterAffinity, overriders policyv1alpha1.Overriders) *policyv1alpha1.OverridePolicy { return &policyv1alpha1.OverridePolicy{