karmada/pkg/util/helper/policy_test.go

408 lines
11 KiB
Go

/*
Copyright 2022 The Karmada Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package helper
import (
"context"
"reflect"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
"github.com/karmada-io/karmada/pkg/util"
)
func TestSetDefaultSpreadConstraints(t *testing.T) {
tests := []struct {
name string
spreadConstraint []policyv1alpha1.SpreadConstraint
expectedSpreadConstraint []policyv1alpha1.SpreadConstraint
}{
{
name: "set spreadByField",
spreadConstraint: []policyv1alpha1.SpreadConstraint{
{
MinGroups: 1,
},
},
expectedSpreadConstraint: []policyv1alpha1.SpreadConstraint{
{
SpreadByField: policyv1alpha1.SpreadByFieldCluster,
MinGroups: 1,
},
},
},
{
name: "set minGroups",
spreadConstraint: []policyv1alpha1.SpreadConstraint{
{
SpreadByField: policyv1alpha1.SpreadByFieldCluster,
},
},
expectedSpreadConstraint: []policyv1alpha1.SpreadConstraint{
{
SpreadByField: policyv1alpha1.SpreadByFieldCluster,
MinGroups: 1,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
SetDefaultSpreadConstraints(tt.spreadConstraint)
if !reflect.DeepEqual(tt.spreadConstraint, tt.expectedSpreadConstraint) {
t.Errorf("expected: %v, but got %v", tt.expectedSpreadConstraint, tt.spreadConstraint)
}
})
}
}
func TestIsDependentOverridesPresent(t *testing.T) {
tests := []struct {
name string
policy *policyv1alpha1.PropagationPolicy
policyCreated bool
expectedExist bool
}{
{
name: "dependent override policy exist",
policy: &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
},
Spec: policyv1alpha1.PropagationSpec{
DependentOverrides: []string{"foo"},
},
},
policyCreated: true,
expectedExist: true,
},
{
name: "dependent override policy do not exist",
policy: &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
},
Spec: policyv1alpha1.PropagationSpec{
DependentOverrides: []string{"foo"},
},
},
policyCreated: false,
expectedExist: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient := fake.NewClientBuilder().WithScheme(Schema).Build()
if tt.policyCreated {
testOverridePolicy := &policyv1alpha1.OverridePolicy{
ObjectMeta: metav1.ObjectMeta{
Namespace: tt.policy.Namespace,
Name: tt.policy.Name,
},
}
err := fakeClient.Create(context.TODO(), testOverridePolicy)
if err != nil {
t.Fatalf("failed to create overridePolicy, err is: %v", err)
}
}
res, err := IsDependentOverridesPresent(fakeClient, tt.policy)
if !reflect.DeepEqual(res, tt.expectedExist) || err != nil {
t.Errorf("expected %v, but got %v", tt.expectedExist, res)
}
})
}
}
func TestIsDependentClusterOverridesPresent(t *testing.T) {
tests := []struct {
name string
policy *policyv1alpha1.ClusterPropagationPolicy
policyCreated bool
expectedExist bool
}{
{
name: "dependent cluster override policy exist",
policy: &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: policyv1alpha1.PropagationSpec{
DependentOverrides: []string{"foo"},
},
},
policyCreated: true,
expectedExist: true,
},
{
name: "dependent cluster override policy do not exist",
policy: &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: policyv1alpha1.PropagationSpec{
DependentOverrides: []string{"foo"},
},
},
policyCreated: false,
expectedExist: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient := fake.NewClientBuilder().WithScheme(Schema).Build()
if tt.policyCreated {
testClusterOverridePolicy := &policyv1alpha1.ClusterOverridePolicy{
ObjectMeta: metav1.ObjectMeta{
Name: tt.policy.Name,
},
}
err := fakeClient.Create(context.TODO(), testClusterOverridePolicy)
if err != nil {
t.Fatalf("failed to create clusterOverridePolicy, err is: %v", err)
}
}
res, err := IsDependentClusterOverridesPresent(fakeClient, tt.policy)
if !reflect.DeepEqual(res, tt.expectedExist) || err != nil {
t.Errorf("expected %v, but got %v", tt.expectedExist, res)
}
})
}
}
func TestCheckMatchServiceImport(t *testing.T) {
tests := []struct {
name string
resourceSelectors []policyv1alpha1.ResourceSelector
expected bool
}{
{
name: " get followed resource selector",
resourceSelectors: []policyv1alpha1.ResourceSelector{
{
Name: "foo1",
Kind: util.ServiceImportKind,
},
{
Name: "foo2",
Namespace: "bar",
Kind: util.ServiceKind,
},
{
Name: "foo3",
Namespace: "bar",
Kind: util.ServiceImportKind,
APIVersion: "multicluster.x-k8s.io/v1alpha1",
},
},
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res := ContainsServiceImport(tt.resourceSelectors)
if !reflect.DeepEqual(res, tt.expected) {
t.Errorf("expected %v, but got %v", tt.expected, res)
}
})
}
}
func TestIsReplicaDynamicDivided(t *testing.T) {
tests := []struct {
name string
strategy *policyv1alpha1.ReplicaSchedulingStrategy
expected bool
}{
{
name: "strategy empty",
strategy: nil,
expected: false,
},
{
name: "strategy duplicated",
strategy: &policyv1alpha1.ReplicaSchedulingStrategy{
ReplicaSchedulingType: policyv1alpha1.ReplicaSchedulingTypeDuplicated,
},
expected: false,
},
{
name: "strategy division preference weighted",
strategy: &policyv1alpha1.ReplicaSchedulingStrategy{
ReplicaDivisionPreference: policyv1alpha1.ReplicaDivisionPreferenceWeighted,
ReplicaSchedulingType: policyv1alpha1.ReplicaSchedulingTypeDivided,
WeightPreference: &policyv1alpha1.ClusterPreferences{
DynamicWeight: policyv1alpha1.DynamicWeightByAvailableReplicas,
},
},
expected: true,
},
{
name: "strategy division preference aggregated",
strategy: &policyv1alpha1.ReplicaSchedulingStrategy{
ReplicaSchedulingType: policyv1alpha1.ReplicaSchedulingTypeDivided,
ReplicaDivisionPreference: policyv1alpha1.ReplicaDivisionPreferenceAggregated,
},
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res := IsReplicaDynamicDivided(&policyv1alpha1.Placement{ReplicaScheduling: tt.strategy})
if res != tt.expected {
t.Errorf("expected %v, but got %v", tt.expected, res)
}
})
}
}
func TestGetAppliedPlacement(t *testing.T) {
tests := []struct {
name string
annotations map[string]string
expectedPlacement *policyv1alpha1.Placement
expectedErr error
}{
{
name: "policy placement annotation exist",
annotations: map[string]string{
util.PolicyPlacementAnnotation: "{\"clusterAffinity\":{\"clusterNames\":[\"member1\",\"member2\"]}}",
},
expectedPlacement: &policyv1alpha1.Placement{
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
ClusterNames: []string{"member1", "member2"},
},
},
expectedErr: nil,
},
{
name: "policy placement annotation do not exist",
annotations: map[string]string{
"foo": "bar",
},
expectedPlacement: nil,
expectedErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := GetAppliedPlacement(tt.annotations)
if !reflect.DeepEqual(res, tt.expectedPlacement) || err != tt.expectedErr {
t.Errorf("expected %v and %v, but got %v and %v", tt.expectedPlacement, tt.expectedErr, res, err)
}
})
}
}
func TestSetReplicaDivisionPreferenceWeighted(t *testing.T) {
tests := []struct {
name string
strategy *policyv1alpha1.ReplicaSchedulingStrategy
expectedWeighted bool
}{
{
name: "no replica scheduling strategy declared",
expectedWeighted: false,
},
{
name: "specified aggregated division preference",
strategy: &policyv1alpha1.ReplicaSchedulingStrategy{
ReplicaSchedulingType: policyv1alpha1.ReplicaSchedulingTypeDivided,
ReplicaDivisionPreference: policyv1alpha1.ReplicaDivisionPreferenceAggregated,
},
expectedWeighted: false,
},
{
name: "unspecified replica division preference",
strategy: &policyv1alpha1.ReplicaSchedulingStrategy{
ReplicaSchedulingType: policyv1alpha1.ReplicaSchedulingTypeDivided,
},
expectedWeighted: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &policyv1alpha1.Placement{ReplicaScheduling: tt.strategy}
SetReplicaDivisionPreferenceWeighted(p)
if (p.ReplicaScheduling != nil &&
p.ReplicaScheduling.ReplicaDivisionPreference == policyv1alpha1.ReplicaDivisionPreferenceWeighted) != tt.expectedWeighted {
t.Errorf("expectedWeighted %v, but got %v", tt.expectedWeighted, !tt.expectedWeighted)
}
})
}
}
func TestSetDefaultGracePeriodSeconds(t *testing.T) {
tests := []struct {
name string
behavior *policyv1alpha1.ApplicationFailoverBehavior
expectBehavior *policyv1alpha1.ApplicationFailoverBehavior
}{
{
name: "purgeMode is not graciously",
behavior: &policyv1alpha1.ApplicationFailoverBehavior{
PurgeMode: policyv1alpha1.Never,
},
expectBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
PurgeMode: policyv1alpha1.Never,
},
},
{
name: "purgeMode is graciously and gracePeriodSeconds is set",
behavior: &policyv1alpha1.ApplicationFailoverBehavior{
PurgeMode: policyv1alpha1.Graciously,
GracePeriodSeconds: ptr.To[int32](200),
},
expectBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
PurgeMode: policyv1alpha1.Graciously,
GracePeriodSeconds: ptr.To[int32](200),
},
},
{
name: "purgeMode is graciously and gracePeriodSeconds is not set",
behavior: &policyv1alpha1.ApplicationFailoverBehavior{
PurgeMode: policyv1alpha1.Graciously,
},
expectBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
PurgeMode: policyv1alpha1.Graciously,
GracePeriodSeconds: ptr.To[int32](600),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
SetDefaultGracePeriodSeconds(tt.behavior)
if !reflect.DeepEqual(tt.behavior, tt.expectBehavior) {
t.Errorf("expectedBehavior %v, but got %v", tt.expectBehavior, tt.behavior)
}
})
}
}