diff --git a/pkg/util/taints/taints_test.go b/pkg/util/taints/taints_test.go index 280cb616d0..c0db4ec577 100644 --- a/pkg/util/taints/taints_test.go +++ b/pkg/util/taints/taints_test.go @@ -112,6 +112,479 @@ func TestAddOrUpdateTaint(t *testing.T) { checkResult("Add Duplicate Taint", newNode, taint, result, false, err) } +func TestTaintExists(t *testing.T) { + testingTaints := []v1.Taint{ + { + Key: "foo_1", + Value: "bar_1", + Effect: v1.TaintEffectNoExecute, + }, + { + Key: "foo_2", + Value: "bar_2", + Effect: v1.TaintEffectNoSchedule, + }, + } + + cases := []struct { + name string + taintToFind *v1.Taint + expectedResult bool + }{ + { + name: "taint exists", + taintToFind: &v1.Taint{Key: "foo_1", Value: "bar_1", Effect: v1.TaintEffectNoExecute}, + expectedResult: true, + }, + { + name: "different key", + taintToFind: &v1.Taint{Key: "no_such_key", Value: "bar_1", Effect: v1.TaintEffectNoExecute}, + expectedResult: false, + }, + { + name: "different effect", + taintToFind: &v1.Taint{Key: "foo_1", Value: "bar_1", Effect: v1.TaintEffectNoSchedule}, + expectedResult: false, + }, + } + + for _, c := range cases { + result := TaintExists(testingTaints, c.taintToFind) + + if result != c.expectedResult { + t.Errorf("[%s] unexpected results: %v", c.name, result) + continue + } + } +} + +func TestRemoveTaint(t *testing.T) { + cases := []struct { + name string + node *v1.Node + taintToRemove *v1.Taint + expectedTaints []v1.Taint + expectedResult bool + }{ + { + name: "remove taint unsuccessfully", + node: &v1.Node{ + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + }, + }, + taintToRemove: &v1.Taint{ + Key: "foo_1", + Effect: v1.TaintEffectNoSchedule, + }, + expectedTaints: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedResult: false, + }, + { + name: "remove taint successfully", + node: &v1.Node{ + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + }, + }, + taintToRemove: &v1.Taint{ + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + expectedTaints: []v1.Taint{}, + expectedResult: true, + }, + { + name: "remove taint from node with no taint", + node: &v1.Node{ + Spec: v1.NodeSpec{ + Taints: []v1.Taint{}, + }, + }, + taintToRemove: &v1.Taint{ + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + expectedTaints: []v1.Taint{}, + expectedResult: false, + }, + } + + for _, c := range cases { + newNode, result, err := RemoveTaint(c.node, c.taintToRemove) + if err != nil { + t.Errorf("[%s] should not raise error but got: %v", c.name, err) + } + if result != c.expectedResult { + t.Errorf("[%s] should return %t, but got: %t", c.name, c.expectedResult, result) + } + if !reflect.DeepEqual(newNode.Spec.Taints, c.expectedTaints) { + t.Errorf("[%s] the new node object should have taints %v, but got: %v", c.name, c.expectedTaints, newNode.Spec.Taints) + } + } +} + +func TestDeleteTaint(t *testing.T) { + cases := []struct { + name string + taints []v1.Taint + taintToDelete *v1.Taint + expectedTaints []v1.Taint + expectedResult bool + }{ + { + name: "delete taint with different name", + taints: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + taintToDelete: &v1.Taint{Key: "foo_1", Effect: v1.TaintEffectNoSchedule}, + expectedTaints: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedResult: false, + }, + { + name: "delete taint with different effect", + taints: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoExecute}, + expectedTaints: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedResult: false, + }, + { + name: "delete taint successfully", + taints: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoSchedule}, + expectedTaints: []v1.Taint{}, + expectedResult: true, + }, + { + name: "delete taint from empty taint array", + taints: []v1.Taint{}, + taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoSchedule}, + expectedTaints: []v1.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 []v1.Taint + taintKey string + expectedTaints []v1.Taint + expectedResult bool + }{ + { + name: "delete taint unsuccessfully", + taints: []v1.Taint{ + { + Key: "foo", + Value: "bar", + Effect: v1.TaintEffectNoSchedule, + }, + }, + taintKey: "foo_1", + expectedTaints: []v1.Taint{ + { + Key: "foo", + Value: "bar", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedResult: false, + }, + { + name: "delete taint successfully", + taints: []v1.Taint{ + { + Key: "foo", + Value: "bar", + Effect: v1.TaintEffectNoSchedule, + }, + }, + taintKey: "foo", + expectedTaints: []v1.Taint{}, + expectedResult: true, + }, + { + name: "delete taint from empty taint array", + taints: []v1.Taint{}, + taintKey: "foo", + expectedTaints: []v1.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 := []v1.Taint{ + { + Key: "foo_1", + Value: "bar", + Effect: v1.TaintEffectNoSchedule, + }, + { + Key: "foo_2", + Value: "bar", + Effect: v1.TaintEffectNoSchedule, + }, + { + Key: "foo_3", + Value: "bar", + Effect: v1.TaintEffectNoSchedule, + }, + } + + cases := []struct { + name string + taintsToCheck []v1.Taint + expectedResult string + }{ + { + name: "empty array", + taintsToCheck: []v1.Taint{}, + expectedResult: "", + }, + { + name: "no match", + taintsToCheck: []v1.Taint{ + { + Key: "foo_1", + Effect: v1.TaintEffectNoExecute, + }, + }, + expectedResult: "", + }, + { + name: "match one taint", + taintsToCheck: []v1.Taint{ + { + Key: "foo_2", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedResult: "foo_2", + }, + { + name: "match two taints", + taintsToCheck: []v1.Taint{ + { + Key: "foo_2", + Effect: v1.TaintEffectNoSchedule, + }, + { + Key: "foo_3", + Effect: v1.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) { + node := &v1.Node{ + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + { + Key: "foo", + Value: "bar", + Effect: v1.TaintEffectNoSchedule, + }, + }, + }, + } + + cases := []struct { + name string + overwrite bool + taintsToAdd []v1.Taint + taintsToDelete []v1.Taint + expectedTaints []v1.Taint + expectedOperation string + expectedErr bool + }{ + { + name: "no changes with overwrite is true", + overwrite: true, + taintsToAdd: []v1.Taint{}, + taintsToDelete: []v1.Taint{}, + expectedTaints: node.Spec.Taints, + expectedOperation: MODIFIED, + expectedErr: false, + }, + { + name: "no changes with overwrite is false", + overwrite: false, + taintsToAdd: []v1.Taint{}, + taintsToDelete: []v1.Taint{}, + expectedTaints: node.Spec.Taints, + expectedOperation: UNTAINTED, + expectedErr: false, + }, + { + name: "add new taint", + overwrite: false, + taintsToAdd: []v1.Taint{ + { + Key: "foo_1", + Effect: v1.TaintEffectNoExecute, + }, + }, + taintsToDelete: []v1.Taint{}, + expectedTaints: append([]v1.Taint{{Key: "foo_1", Effect: v1.TaintEffectNoExecute}}, node.Spec.Taints...), + expectedOperation: TAINTED, + expectedErr: false, + }, + { + name: "delete taint with effect", + overwrite: false, + taintsToAdd: []v1.Taint{}, + taintsToDelete: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedTaints: []v1.Taint{}, + expectedOperation: UNTAINTED, + expectedErr: false, + }, + { + name: "delete taint with no effect", + overwrite: false, + taintsToAdd: []v1.Taint{}, + taintsToDelete: []v1.Taint{ + { + Key: "foo", + }, + }, + expectedTaints: []v1.Taint{}, + expectedOperation: UNTAINTED, + expectedErr: false, + }, + { + name: "delete non-exist taint", + overwrite: false, + taintsToAdd: []v1.Taint{}, + taintsToDelete: []v1.Taint{ + { + Key: "foo_1", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedTaints: node.Spec.Taints, + expectedOperation: UNTAINTED, + expectedErr: true, + }, + { + name: "add new taint and delete old one", + overwrite: false, + taintsToAdd: []v1.Taint{ + { + Key: "foo_1", + Effect: v1.TaintEffectNoSchedule, + }, + }, + taintsToDelete: []v1.Taint{ + { + Key: "foo", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedTaints: []v1.Taint{ + { + Key: "foo_1", + Effect: v1.TaintEffectNoSchedule, + }, + }, + expectedOperation: MODIFIED, + expectedErr: false, + }, + } + + for _, c := range cases { + operation, taints, err := ReorganizeTaints(node, 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