package karmadactl import ( "reflect" "testing" corev1 "k8s.io/api/core/v1" clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" ) func TestValidate(t *testing.T) { taintsToAdd := []corev1.Taint{{Key: "foo", Value: "bar", Effect: corev1.TaintEffectNoSchedule}} taintsToRemove := []corev1.Taint{{Key: "foo", Value: "bar", Effect: corev1.TaintEffectNoSchedule}} cases := []struct { taintOpt CommandTaintOption description string expectFatal bool }{ { taintOpt: CommandTaintOption{resources: []string{"cluster"}}, description: "Cluster name is not provided", expectFatal: true, }, { taintOpt: CommandTaintOption{resources: []string{"node"}}, description: "invalid resource type", expectFatal: true, }, { taintOpt: CommandTaintOption{resources: []string{"cluster", "cluster-name"}, taintsToAdd: taintsToAdd, taintsToRemove: taintsToRemove}, description: "both modify and remove the following taint(s) in the same command", expectFatal: true, }, { taintOpt: CommandTaintOption{resources: []string{"cluster", "cluster-name"}}, description: "Cluster name is provided", expectFatal: false, }, } for _, c := range cases { sawFatal := false err := c.taintOpt.Validate() if err != nil { sawFatal = true } if c.expectFatal { if !sawFatal { t.Fatalf("%s expected not to fail", c.description) } } } } func TestParseTaintArgs(t *testing.T) { cases := []struct { taintOpt CommandTaintOption taintArgs []string description string expectFatal bool }{ { taintOpt: CommandTaintOption{}, taintArgs: []string{"cluster", "cluster_name", "foo=bar:NoSchedule", "foo_1=bar:NoExecute"}, description: "Correct order of arguments", expectFatal: false, }, { taintOpt: CommandTaintOption{}, taintArgs: []string{"cluster", "foo=bar:NoSchedule", "cluster_name", "foo_1=bar:NoExecute"}, description: "Incorrect order of arguments", expectFatal: true, }, } for _, c := range cases { sawFatal := false _, err := c.taintOpt.parseTaintArgs(c.taintArgs) if err != nil { sawFatal = true } if c.expectFatal { if !sawFatal { t.Fatalf("%s expected not to fail", c.description) } } } } func TestDeleteTaint(t *testing.T) { cases := []struct { name string taints []corev1.Taint taintToDelete *corev1.Taint expectedTaints []corev1.Taint expectedResult bool }{ { name: "delete taint with different name", taints: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, }, taintToDelete: &corev1.Taint{Key: "foo_1", Effect: corev1.TaintEffectNoSchedule}, expectedTaints: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, }, expectedResult: false, }, { name: "delete taint with different effect", taints: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, }, taintToDelete: &corev1.Taint{Key: "foo", Effect: corev1.TaintEffectNoExecute}, expectedTaints: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, }, expectedResult: false, }, { name: "delete taint successfully", taints: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, }, taintToDelete: &corev1.Taint{Key: "foo", Effect: corev1.TaintEffectNoSchedule}, expectedTaints: []corev1.Taint{}, expectedResult: true, }, { name: "delete taint from empty taint array", taints: []corev1.Taint{}, taintToDelete: &corev1.Taint{Key: "foo", Effect: corev1.TaintEffectNoSchedule}, expectedTaints: []corev1.Taint{}, expectedResult: false, }, } for _, c := range cases { taints, result := deleteTaint(c.taints, c.taintToDelete) if result != c.expectedResult { t.Errorf("[%s] should return %t, but, got: %t", c.name, c.expectedResult, result) } if !reflect.DeepEqual(taints, c.expectedTaints) { t.Errorf("[%s] the result taints should be %v, but got: %v", c.name, c.expectedTaints, taints) } } } func TestDeleteTaintByKey(t *testing.T) { cases := []struct { name string taints []corev1.Taint taintKey string expectedTaints []corev1.Taint expectedResult bool }{ { name: "delete taint unsuccessfully", taints: []corev1.Taint{ { Key: "foo", Value: "bar", Effect: corev1.TaintEffectNoSchedule, }, }, taintKey: "foo_1", expectedTaints: []corev1.Taint{ { Key: "foo", Value: "bar", Effect: corev1.TaintEffectNoSchedule, }, }, expectedResult: false, }, { name: "delete taint successfully", taints: []corev1.Taint{ { Key: "foo", Value: "bar", Effect: corev1.TaintEffectNoSchedule, }, }, taintKey: "foo", expectedTaints: []corev1.Taint{}, expectedResult: true, }, { name: "delete taint from empty taint array", taints: []corev1.Taint{}, taintKey: "foo", expectedTaints: []corev1.Taint{}, expectedResult: false, }, } for _, c := range cases { taints, result := deleteTaintsByKey(c.taints, c.taintKey) if result != c.expectedResult { t.Errorf("[%s] should return %t, but got: %t", c.name, c.expectedResult, result) } if !reflect.DeepEqual(c.expectedTaints, taints) { t.Errorf("[%s] the result taints should be %v, but got: %v", c.name, c.expectedTaints, taints) } } } func TestCheckIfTaintsAlreadyExists(t *testing.T) { oldTaints := []corev1.Taint{ { Key: "foo_1", Value: "bar", Effect: corev1.TaintEffectNoSchedule, }, { Key: "foo_2", Value: "bar", Effect: corev1.TaintEffectNoSchedule, }, { Key: "foo_3", Value: "bar", Effect: corev1.TaintEffectNoSchedule, }, } cases := []struct { name string taintsToCheck []corev1.Taint expectedResult string }{ { name: "empty array", taintsToCheck: []corev1.Taint{}, expectedResult: "", }, { name: "no match", taintsToCheck: []corev1.Taint{ { Key: "foo_1", Effect: corev1.TaintEffectNoExecute, }, }, expectedResult: "", }, { name: "match one taint", taintsToCheck: []corev1.Taint{ { Key: "foo_2", Effect: corev1.TaintEffectNoSchedule, }, }, expectedResult: "foo_2", }, { name: "match two taints", taintsToCheck: []corev1.Taint{ { Key: "foo_2", Effect: corev1.TaintEffectNoSchedule, }, { Key: "foo_3", Effect: corev1.TaintEffectNoSchedule, }, }, expectedResult: "foo_2,foo_3", }, } for _, c := range cases { result := checkIfTaintsAlreadyExists(oldTaints, c.taintsToCheck) if result != c.expectedResult { t.Errorf("[%s] should return '%s', but got: '%s'", c.name, c.expectedResult, result) } } } func TestReorganizeTaints(t *testing.T) { cluster := &clusterv1alpha1.Cluster{ Spec: clusterv1alpha1.ClusterSpec{ Taints: []corev1.Taint{ { Key: "foo", Value: "bar", Effect: corev1.TaintEffectNoSchedule, }, }, }, } cases := []struct { name string overwrite bool taintsToAdd []corev1.Taint taintsToDelete []corev1.Taint expectedTaints []corev1.Taint expectedOperation string expectedErr bool }{ { name: "no changes with overwrite is true", overwrite: true, taintsToAdd: []corev1.Taint{}, taintsToDelete: []corev1.Taint{}, expectedTaints: cluster.Spec.Taints, expectedOperation: MODIFIED, expectedErr: false, }, { name: "no changes with overwrite is false", overwrite: false, taintsToAdd: []corev1.Taint{}, taintsToDelete: []corev1.Taint{}, expectedTaints: cluster.Spec.Taints, expectedOperation: UNTAINTED, expectedErr: false, }, { name: "add new taint", overwrite: false, taintsToAdd: []corev1.Taint{ { Key: "foo_1", Effect: corev1.TaintEffectNoExecute, }, }, taintsToDelete: []corev1.Taint{}, expectedTaints: append([]corev1.Taint{{Key: "foo_1", Effect: corev1.TaintEffectNoExecute}}, cluster.Spec.Taints...), expectedOperation: TAINTED, expectedErr: false, }, { name: "delete taint with effect", overwrite: false, taintsToAdd: []corev1.Taint{}, taintsToDelete: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, }, expectedTaints: []corev1.Taint{}, expectedOperation: UNTAINTED, expectedErr: false, }, { name: "delete taint with no effect", overwrite: false, taintsToAdd: []corev1.Taint{}, taintsToDelete: []corev1.Taint{ { Key: "foo", }, }, expectedTaints: []corev1.Taint{}, expectedOperation: UNTAINTED, expectedErr: false, }, { name: "delete non-exist taint", overwrite: false, taintsToAdd: []corev1.Taint{}, taintsToDelete: []corev1.Taint{ { Key: "foo_1", Effect: corev1.TaintEffectNoSchedule, }, }, expectedTaints: cluster.Spec.Taints, expectedOperation: UNTAINTED, expectedErr: true, }, { name: "add new taint and delete old one", overwrite: false, taintsToAdd: []corev1.Taint{ { Key: "foo_1", Effect: corev1.TaintEffectNoSchedule, }, }, taintsToDelete: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, }, expectedTaints: []corev1.Taint{ { Key: "foo_1", Effect: corev1.TaintEffectNoSchedule, }, }, expectedOperation: MODIFIED, expectedErr: false, }, } for _, c := range cases { operation, taints, err := reorganizeTaints(cluster, c.overwrite, c.taintsToAdd, c.taintsToDelete) if c.expectedErr && err == nil { t.Errorf("[%s] expect to see an error, but did not get one", c.name) } else if !c.expectedErr && err != nil { t.Errorf("[%s] expect not to see an error, but got one: %v", c.name, err) } if !reflect.DeepEqual(c.expectedTaints, taints) { t.Errorf("[%s] expect to see taint list %#v, but got: %#v", c.name, c.expectedTaints, taints) } if c.expectedOperation != operation { t.Errorf("[%s] expect to see operation %s, but got: %s", c.name, c.expectedOperation, operation) } } } func TestParseTaints(t *testing.T) { cases := []struct { name string spec []string expectedTaints []corev1.Taint expectedTaintsToRemove []corev1.Taint expectedErr bool }{ { name: "invalid spec format", spec: []string{""}, expectedErr: true, }, { name: "invalid spec format", spec: []string{"foo=abc"}, expectedErr: true, }, { name: "invalid spec format", spec: []string{"foo=abc=xyz:NoSchedule"}, expectedErr: true, }, { name: "invalid spec format", spec: []string{"foo=abc:xyz:NoSchedule"}, expectedErr: true, }, { name: "invalid spec format for adding taint", spec: []string{"foo"}, expectedErr: true, }, { name: "invalid spec effect for adding taint", spec: []string{"foo=abc:invalid_effect"}, expectedErr: true, }, { name: "invalid spec effect for deleting taint", spec: []string{"foo:invalid_effect-"}, expectedErr: true, }, { name: "add new taints", spec: []string{"foo=abc:NoSchedule", "bar=abc:NoSchedule", "baz:NoSchedule", "qux:NoSchedule", "foobar=:NoSchedule"}, expectedTaints: []corev1.Taint{ { Key: "foo", Value: "abc", Effect: corev1.TaintEffectNoSchedule, }, { Key: "bar", Value: "abc", Effect: corev1.TaintEffectNoSchedule, }, { Key: "baz", Value: "", Effect: corev1.TaintEffectNoSchedule, }, { Key: "qux", Value: "", Effect: corev1.TaintEffectNoSchedule, }, { Key: "foobar", Value: "", Effect: corev1.TaintEffectNoSchedule, }, }, expectedErr: false, }, { name: "delete taints", spec: []string{"foo:NoSchedule-", "bar:NoSchedule-", "qux=:NoSchedule-", "dedicated-"}, expectedTaintsToRemove: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, { Key: "bar", Effect: corev1.TaintEffectNoSchedule, }, { Key: "qux", Effect: corev1.TaintEffectNoSchedule, }, { Key: "dedicated", }, }, expectedErr: false, }, { name: "add taints and delete taints", spec: []string{"foo=abc:NoSchedule", "bar=abc:NoSchedule", "baz:NoSchedule", "qux:NoSchedule", "foobar=:NoSchedule", "foo:NoSchedule-", "bar:NoSchedule-", "baz=:NoSchedule-"}, expectedTaints: []corev1.Taint{ { Key: "foo", Value: "abc", Effect: corev1.TaintEffectNoSchedule, }, { Key: "bar", Value: "abc", Effect: corev1.TaintEffectNoSchedule, }, { Key: "baz", Value: "", Effect: corev1.TaintEffectNoSchedule, }, { Key: "qux", Value: "", Effect: corev1.TaintEffectNoSchedule, }, { Key: "foobar", Value: "", Effect: corev1.TaintEffectNoSchedule, }, }, expectedTaintsToRemove: []corev1.Taint{ { Key: "foo", Effect: corev1.TaintEffectNoSchedule, }, { Key: "bar", Effect: corev1.TaintEffectNoSchedule, }, { Key: "baz", Value: "", Effect: corev1.TaintEffectNoSchedule, }, }, expectedErr: false, }, } for _, c := range cases { taints, taintsToRemove, err := parseTaints(c.spec) if c.expectedErr && err == nil { t.Errorf("[%s] expected error for spec %s, but got nothing", c.name, c.spec) } if !c.expectedErr && err != nil { t.Errorf("[%s] expected no error for spec %s, but got: %v", c.name, c.spec, err) } if !reflect.DeepEqual(c.expectedTaints, taints) { t.Errorf("[%s] expected returen taints as %v, but got: %v", c.name, c.expectedTaints, taints) } if !reflect.DeepEqual(c.expectedTaintsToRemove, taintsToRemove) { t.Errorf("[%s] expected return taints to be removed as %v, but got: %v", c.name, c.expectedTaintsToRemove, taintsToRemove) } } }