Merge pull request #81732 from tallclair/merge-tolerations

Fix toleration comparison & merging logic
This commit is contained in:
Kubernetes Prow Robot 2019-08-27 09:25:09 -07:00 committed by GitHub
commit 0eb1bfc9b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 472 additions and 168 deletions

View File

@ -244,7 +244,6 @@ pkg/util/rlimit
pkg/util/selinux
pkg/util/sysctl/testing
pkg/util/taints
pkg/util/tolerations
pkg/version/verflag
pkg/volume
pkg/volume/azure_dd

View File

@ -13,7 +13,11 @@ go_library(
"tolerations.go",
],
importpath = "k8s.io/kubernetes/pkg/util/tolerations",
deps = ["//pkg/apis/core:go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)
filegroup(
@ -33,5 +37,12 @@ go_test(
name = "go_default_test",
srcs = ["tolerations_test.go"],
embed = [":go_default_library"],
deps = ["//pkg/apis/core:go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/validation:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/k8s.io/utils/pointer:go_default_library",
],
)

View File

@ -17,114 +17,91 @@ limitations under the License.
package tolerations
import (
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/klog"
api "k8s.io/kubernetes/pkg/apis/core"
)
type key struct {
tolerationKey string
effect api.TaintEffect
}
func convertTolerationToKey(in api.Toleration) key {
return key{in.Key, in.Effect}
}
// VerifyAgainstWhitelist checks if the provided tolerations
// satisfy the provided whitelist and returns true, otherwise returns false
func VerifyAgainstWhitelist(tolerations []api.Toleration, whitelist []api.Toleration) bool {
if len(whitelist) == 0 {
func VerifyAgainstWhitelist(tolerations, whitelist []api.Toleration) bool {
if len(whitelist) == 0 || len(tolerations) == 0 {
return true
}
t := ConvertTolerationToAMap(tolerations)
w := ConvertTolerationToAMap(whitelist)
for k1, v1 := range t {
if v2, ok := w[k1]; !ok || !AreEqual(v1, v2) {
return false
next:
for _, t := range tolerations {
for _, w := range whitelist {
if isSuperset(w, t) {
continue next
}
}
}
return true
}
// IsConflict returns true if the key of two tolerations match
// but one or more other fields differ, otherwise returns false
func IsConflict(first []api.Toleration, second []api.Toleration) bool {
firstMap := ConvertTolerationToAMap(first)
secondMap := ConvertTolerationToAMap(second)
for k1, v1 := range firstMap {
if v2, ok := secondMap[k1]; ok && !AreEqual(v1, v2) {
return true
}
}
return false
}
// MergeTolerations merges two sets of tolerations into one
// it does not check for conflicts
func MergeTolerations(first []api.Toleration, second []api.Toleration) []api.Toleration {
var mergedTolerations []api.Toleration
mergedTolerations = append(mergedTolerations, second...)
firstMap := ConvertTolerationToAMap(first)
secondMap := ConvertTolerationToAMap(second)
// preserve order of first when merging
for _, v := range first {
k := convertTolerationToKey(v)
// if first contains key conflicts, the last one takes precedence
if _, ok := secondMap[k]; !ok && firstMap[k] == v {
mergedTolerations = append(mergedTolerations, v)
}
}
return mergedTolerations
}
// EqualTolerations returns true if two sets of tolerations are equal, otherwise false
// it assumes no duplicates in individual set of tolerations
func EqualTolerations(first []api.Toleration, second []api.Toleration) bool {
if len(first) != len(second) {
return false
}
firstMap := ConvertTolerationToAMap(first)
secondMap := ConvertTolerationToAMap(second)
for k1, v1 := range firstMap {
if v2, ok := secondMap[k1]; !ok || !AreEqual(v1, v2) {
return false
}
}
return true
}
// ConvertTolerationToAMap converts toleration list into a map[string]api.Toleration
func ConvertTolerationToAMap(in []api.Toleration) map[key]api.Toleration {
out := map[key]api.Toleration{}
for _, v := range in {
out[convertTolerationToKey(v)] = v
// MergeTolerations merges two sets of tolerations into one. If one toleration is a superset of
// another, only the superset is kept.
func MergeTolerations(first, second []api.Toleration) []api.Toleration {
all := append(first, second...)
var merged []api.Toleration
next:
for i, t := range all {
for _, t2 := range merged {
if isSuperset(t2, t) {
continue next // t is redundant; ignore it
}
}
if i+1 < len(all) {
for _, t2 := range all[i+1:] {
// If the tolerations are equal, prefer the first.
if !apiequality.Semantic.DeepEqual(&t, &t2) && isSuperset(t2, t) {
continue next // t is redundant; ignore it
}
}
}
merged = append(merged, t)
}
return out
return merged
}
// AreEqual checks if two provided tolerations are equal or not.
func AreEqual(first, second api.Toleration) bool {
if first.Key == second.Key &&
first.Operator == second.Operator &&
first.Value == second.Value &&
first.Effect == second.Effect &&
AreTolerationSecondsEqual(first.TolerationSeconds, second.TolerationSeconds) {
// isSuperset checks whether ss tolerates a superset of t.
func isSuperset(ss, t api.Toleration) bool {
if apiequality.Semantic.DeepEqual(&t, &ss) {
return true
}
return false
}
// AreTolerationSecondsEqual checks if two provided TolerationSeconds are equal or not.
func AreTolerationSecondsEqual(ts1, ts2 *int64) bool {
if ts1 == ts2 {
return true
if t.Key != ss.Key &&
// An empty key with Exists operator means match all keys & values.
(ss.Key != "" || ss.Operator != api.TolerationOpExists) {
return false
}
if ts1 != nil && ts2 != nil && *ts1 == *ts2 {
return true
// An empty effect means match all effects.
if t.Effect != ss.Effect && ss.Effect != "" {
return false
}
if ss.Effect == api.TaintEffectNoExecute {
if ss.TolerationSeconds != nil {
if t.TolerationSeconds == nil ||
*t.TolerationSeconds > *ss.TolerationSeconds {
return false
}
}
}
switch ss.Operator {
case api.TolerationOpEqual, "": // empty operator means Equal
return t.Operator == api.TolerationOpEqual && t.Value == ss.Value
case api.TolerationOpExists:
return true
default:
klog.Errorf("Unknown toleration operator: %s", ss.Operator)
return false
}
return false
}

View File

@ -17,65 +17,397 @@ limitations under the License.
package tolerations
import (
api "k8s.io/kubernetes/pkg/apis/core"
"encoding/json"
"fmt"
"math/rand"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/util/validation/field"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation"
utilpointer "k8s.io/utils/pointer"
)
var (
tolerations = map[string]api.Toleration{
"all": {Operator: api.TolerationOpExists},
"all-nosched": {
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoSchedule,
},
"all-noexec": {
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoExecute,
},
"foo": {
Key: "foo",
Operator: api.TolerationOpExists,
},
"foo-bar": {
Key: "foo",
Operator: api.TolerationOpEqual,
Value: "bar",
},
"foo-nosched": {
Key: "foo",
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoSchedule,
},
"foo-bar-nosched": {
Key: "foo",
Operator: api.TolerationOpEqual,
Value: "bar",
Effect: api.TaintEffectNoSchedule,
},
"foo-baz-nosched": {
Key: "foo",
Operator: api.TolerationOpEqual,
Value: "baz",
Effect: api.TaintEffectNoSchedule,
},
"faz-nosched": {
Key: "faz",
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoSchedule,
},
"faz-baz-nosched": {
Key: "faz",
Operator: api.TolerationOpEqual,
Value: "baz",
Effect: api.TaintEffectNoSchedule,
},
"foo-prefnosched": {
Key: "foo",
Operator: api.TolerationOpExists,
Effect: api.TaintEffectPreferNoSchedule,
},
"foo-noexec": {
Key: "foo",
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoExecute,
},
"foo-bar-noexec": {
Key: "foo",
Operator: api.TolerationOpEqual,
Value: "bar",
Effect: api.TaintEffectNoExecute,
},
"foo-noexec-10": {
Key: "foo",
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoExecute,
TolerationSeconds: utilpointer.Int64Ptr(10),
},
"foo-noexec-0": {
Key: "foo",
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoExecute,
TolerationSeconds: utilpointer.Int64Ptr(0),
},
"foo-bar-noexec-10": {
Key: "foo",
Operator: api.TolerationOpEqual,
Value: "bar",
Effect: api.TaintEffectNoExecute,
TolerationSeconds: utilpointer.Int64Ptr(10),
},
}
)
func TestIsSuperset(t *testing.T) {
tests := []struct {
toleration string
ss []string // t should be a superset of these
}{{
"all",
[]string{"all-nosched", "all-noexec", "foo", "foo-bar", "foo-nosched", "foo-bar-nosched", "foo-baz-nosched", "faz-nosched", "faz-baz-nosched", "foo-prefnosched", "foo-noexec", "foo-bar-noexec", "foo-noexec-10", "foo-noexec-0", "foo-bar-noexec-10"},
}, {
"all-nosched",
[]string{"foo-nosched", "foo-bar-nosched", "foo-baz-nosched", "faz-nosched", "faz-baz-nosched"},
}, {
"all-noexec",
[]string{"foo-noexec", "foo-bar-noexec", "foo-noexec-10", "foo-noexec-0", "foo-bar-noexec-10"},
}, {
"foo",
[]string{"foo-bar", "foo-nosched", "foo-bar-nosched", "foo-baz-nosched", "foo-prefnosched", "foo-noexec", "foo-bar-noexec", "foo-noexec-10", "foo-noexec-0", "foo-bar-noexec-10"},
}, {
"foo-bar",
[]string{"foo-bar-nosched", "foo-bar-noexec", "foo-bar-noexec-10"},
}, {
"foo-nosched",
[]string{"foo-bar-nosched", "foo-baz-nosched"},
}, {
"foo-bar-nosched",
[]string{},
}, {
"faz-nosched",
[]string{"faz-baz-nosched"},
}, {
"faz-baz-nosched",
[]string{},
}, {
"foo-prenosched",
[]string{},
}, {
"foo-noexec",
[]string{"foo-noexec", "foo-bar-noexec", "foo-noexec-10", "foo-noexec-0", "foo-bar-noexec-10"},
}, {
"foo-bar-noexec",
[]string{"foo-bar-noexec-10"},
}, {
"foo-noexec-10",
[]string{"foo-noexec-0", "foo-bar-noexec-10"},
}, {
"foo-noexec-0",
[]string{},
}, {
"foo-bar-noexec-10",
[]string{},
}}
assertSuperset := func(t *testing.T, super, sub string) {
assert.True(t, isSuperset(tolerations[super], tolerations[sub]),
"%s should be a superset of %s", super, sub)
}
assertNotSuperset := func(t *testing.T, super, sub string) {
assert.False(t, isSuperset(tolerations[super], tolerations[sub]),
"%s should NOT be a superset of %s", super, sub)
}
contains := func(ss []string, s string) bool {
for _, str := range ss {
if str == s {
return true
}
}
return false
}
for _, test := range tests {
t.Run(test.toleration, func(t *testing.T) {
for name := range tolerations {
if name == test.toleration || contains(test.ss, name) {
assertSuperset(t, test.toleration, name)
} else {
assertNotSuperset(t, test.toleration, name)
}
}
})
}
}
func TestVerifyAgainstWhitelist(t *testing.T) {
tests := []struct {
input []api.Toleration
whitelist []api.Toleration
testName string
testStatus bool
testName string
input []string
whitelist []string
expected bool
}{
{
input: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
whitelist: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
testName: "equal input and whitelist",
testStatus: true,
testName: "equal input and whitelist",
input: []string{"foo-bar-nosched", "foo-baz-nosched"},
whitelist: []string{"foo-bar-nosched", "foo-baz-nosched"},
expected: true,
},
{
input: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
whitelist: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute"}},
testName: "input does not exist in whitelist",
testStatus: false,
testName: "duplicate input allowed",
input: []string{"foo-bar-nosched", "foo-bar-nosched"},
whitelist: []string{"foo-bar-nosched", "foo-baz-nosched"},
expected: true,
},
{
testName: "allow all",
input: []string{"foo-bar-nosched", "foo-bar-nosched"},
whitelist: []string{"all"},
expected: true,
},
{
testName: "duplicate input forbidden",
input: []string{"foo-bar-nosched", "foo-bar-nosched"},
whitelist: []string{"foo-baz-nosched"},
expected: false,
},
{
testName: "value mismatch",
input: []string{"foo-bar-nosched", "foo-baz-nosched"},
whitelist: []string{"foo-baz-nosched"},
expected: false,
},
{
testName: "input does not exist in whitelist",
input: []string{"foo-bar-nosched"},
whitelist: []string{"foo-baz-nosched"},
expected: false,
},
{
testName: "disjoint sets",
input: []string{"foo-bar"},
whitelist: []string{"foo-nosched"},
expected: false,
},
{
testName: "empty whitelist",
input: []string{"foo-bar"},
whitelist: []string{},
expected: true,
},
{
testName: "empty input",
input: []string{},
whitelist: []string{"foo-bar"},
expected: true,
},
}
for _, c := range tests {
status := VerifyAgainstWhitelist(c.input, c.whitelist)
if status != c.testStatus {
t.Errorf("Test: %s, expected %v", c.testName, status)
}
t.Run(c.testName, func(t *testing.T) {
actual := VerifyAgainstWhitelist(getTolerations(c.input), getTolerations(c.whitelist))
assert.Equal(t, c.expected, actual)
})
}
}
func TestIsConflict(t *testing.T) {
func TestMergeTolerations(t *testing.T) {
tests := []struct {
input1 []api.Toleration
input2 []api.Toleration
testName string
testStatus bool
}{
{
input1: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
input2: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
testName: "equal inputs",
testStatus: true,
},
{
input1: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "foo", Effect: "NoExecute"}},
input2: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute"}},
testName: "mismatch values in inputs",
testStatus: false,
},
}
name string
a, b []string
expected []string
}{{
name: "disjoint",
a: []string{"foo-bar-nosched", "faz-baz-nosched", "foo-noexec-10"},
b: []string{"foo-prefnosched", "foo-baz-nosched"},
expected: []string{"foo-bar-nosched", "faz-baz-nosched", "foo-noexec-10", "foo-prefnosched", "foo-baz-nosched"},
}, {
name: "duplicate",
a: []string{"foo-bar-nosched", "faz-baz-nosched", "foo-noexec-10"},
b: []string{"foo-bar-nosched", "faz-baz-nosched", "foo-noexec-10"},
expected: []string{"foo-bar-nosched", "faz-baz-nosched", "foo-noexec-10"},
}, {
name: "merge redundant",
a: []string{"foo-bar-nosched", "foo-baz-nosched"},
b: []string{"foo-nosched", "faz-baz-nosched"},
expected: []string{"foo-nosched", "faz-baz-nosched"},
}, {
name: "merge all",
a: []string{"foo-bar-nosched", "foo-baz-nosched", "foo-noexec-10"},
b: []string{"all"},
expected: []string{"all"},
}, {
name: "merge into all",
a: []string{"all"},
b: []string{"foo-bar-nosched", "foo-baz-nosched", "foo-noexec-10"},
expected: []string{"all"},
}}
for _, c := range tests {
status := IsConflict(c.input1, c.input2)
if status == c.testStatus {
t.Errorf("Test: %s, expected %v", c.testName, status)
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := MergeTolerations(getTolerations(test.a), getTolerations(test.b))
require.Len(t, actual, len(test.expected))
for i, expect := range getTolerations(test.expected) {
assert.Equal(t, expect, actual[i], "expected[%d] = %s", i, test.expected[i])
}
})
}
}
func TestFuzzed(t *testing.T) {
r := rand.New(rand.NewSource(1234)) // Fixed source to prevent flakes.
const (
allProbability = 0.01 // Chance of getting a tolerate all
existsProbability = 0.3
tolerationSecondsProbability = 0.5
)
effects := []api.TaintEffect{"", api.TaintEffectNoExecute, api.TaintEffectNoSchedule, api.TaintEffectPreferNoSchedule}
genToleration := func() api.Toleration {
gen := api.Toleration{
Effect: effects[r.Intn(len(effects))],
}
if r.Float32() < allProbability {
gen = tolerations["all"]
return gen
}
// Small key/value space to encourage collisions
gen.Key = strings.Repeat("a", r.Intn(6)+1)
if r.Float32() < existsProbability {
gen.Operator = api.TolerationOpExists
} else {
gen.Operator = api.TolerationOpEqual
gen.Value = strings.Repeat("b", r.Intn(6)+1)
}
if gen.Effect == api.TaintEffectNoExecute && r.Float32() < tolerationSecondsProbability {
gen.TolerationSeconds = utilpointer.Int64Ptr(r.Int63n(10))
}
// Ensure only valid tolerations are generated.
require.NoError(t, validation.ValidateTolerations([]api.Toleration{gen}, field.NewPath("")).ToAggregate(), "%#v", gen)
return gen
}
genTolerations := func() []api.Toleration {
result := []api.Toleration{}
for i := 0; i < r.Intn(10); i++ {
result = append(result, genToleration())
}
return result
}
// Check whether the toleration is a subset of a toleration in the set.
isContained := func(toleration api.Toleration, set []api.Toleration) bool {
for _, ss := range set {
if isSuperset(ss, toleration) {
return true
}
}
return false
}
const iterations = 1000
debugMsg := func(tolerations ...[]api.Toleration) string {
str, err := json.Marshal(tolerations)
if err != nil {
return fmt.Sprintf("[ERR: %v] %v", err, tolerations)
}
return string(str)
}
t.Run("VerifyAgainstWhitelist", func(t *testing.T) {
for i := 0; i < iterations; i++ {
input := genTolerations()
whitelist := append(genTolerations(), genToleration()) // Non-empty
if VerifyAgainstWhitelist(input, whitelist) {
for _, tol := range input {
require.True(t, isContained(tol, whitelist), debugMsg(input, whitelist))
}
} else {
uncontained := false
for _, tol := range input {
if !isContained(tol, whitelist) {
uncontained = true
break
}
}
require.True(t, uncontained, debugMsg(input, whitelist))
}
}
})
t.Run("MergeTolerations", func(t *testing.T) {
for i := 0; i < iterations; i++ {
a := genTolerations()
b := genTolerations()
result := MergeTolerations(a, b)
for _, tol := range append(a, b...) {
require.True(t, isContained(tol, result), debugMsg(a, b, result))
}
}
})
}
func getTolerations(names []string) []api.Toleration {
result := []api.Toleration{}
for _, name := range names {
result = append(result, tolerations[name])
}
return result
}

View File

@ -14,7 +14,6 @@ go_test(
"//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//pkg/scheduler/api:go_default_library",
"//pkg/util/tolerations:go_default_library",
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
@ -27,6 +26,7 @@ go_test(
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)

View File

@ -84,7 +84,7 @@ func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.
}
pod := a.GetObject().(*api.Pod)
var finalTolerations []api.Toleration
var extraTolerations []api.Toleration
if a.GetOperation() == admission.Create {
ts, err := p.getNamespaceDefaultTolerations(a.GetNamespace())
if err != nil {
@ -97,37 +97,20 @@ func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.
ts = p.pluginConfig.Default
}
if len(ts) > 0 {
if len(pod.Spec.Tolerations) > 0 {
if tolerations.IsConflict(ts, pod.Spec.Tolerations) {
return fmt.Errorf("namespace tolerations and pod tolerations conflict")
}
// modified pod tolerations = namespace tolerations + current pod tolerations
finalTolerations = tolerations.MergeTolerations(ts, pod.Spec.Tolerations)
} else {
finalTolerations = ts
}
} else {
finalTolerations = pod.Spec.Tolerations
}
} else {
finalTolerations = pod.Spec.Tolerations
extraTolerations = ts
}
if qoshelper.GetPodQOS(pod) != api.PodQOSBestEffort {
finalTolerations = tolerations.MergeTolerations(finalTolerations, []api.Toleration{
{
Key: schedulerapi.TaintNodeMemoryPressure,
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoSchedule,
},
extraTolerations = append(extraTolerations, api.Toleration{
Key: schedulerapi.TaintNodeMemoryPressure,
Operator: api.TolerationOpExists,
Effect: api.TaintEffectNoSchedule,
})
}
// Final merge of tolerations irrespective of pod type, if the user while creating pods gives
// conflicting tolerations(with same key+effect), the existing ones should be overwritten by latest one
pod.Spec.Tolerations = tolerations.MergeTolerations(finalTolerations, []api.Toleration{})
// Final merge of tolerations irrespective of pod type.
if len(extraTolerations) > 0 {
pod.Spec.Tolerations = tolerations.MergeTolerations(pod.Spec.Tolerations, extraTolerations)
}
return p.Validate(ctx, a, o)
}

View File

@ -22,6 +22,7 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -36,7 +37,6 @@ import (
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
"k8s.io/kubernetes/pkg/util/tolerations"
pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
)
@ -139,10 +139,11 @@ func TestPodAdmission(t *testing.T) {
{
pod: bestEffortPod,
defaultClusterTolerations: []api.Toleration{},
namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}},
podTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil}},
admit: false,
testName: "conflicting pod and namespace tolerations",
namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule"}},
podTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule"}},
mergedTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule"}, {Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule"}},
admit: true,
testName: "duplicate key pod and namespace tolerations",
},
{
pod: bestEffortPod,
@ -210,10 +211,11 @@ func TestPodAdmission(t *testing.T) {
whitelist: []api.Toleration{},
podTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}, {Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil}},
mergedTolerations: []api.Toleration{
{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil},
{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil},
},
admit: true,
testName: "Besteffort pod should overwrite for conflicting tolerations",
testName: "Pod with duplicate key tolerations should not be modified",
},
{
pod: guaranteedPod,
@ -275,8 +277,8 @@ func TestPodAdmission(t *testing.T) {
}
updatedPodTolerations := pod.Spec.Tolerations
if test.admit && !tolerations.EqualTolerations(updatedPodTolerations, test.mergedTolerations) {
t.Errorf("Test: %s, expected: %#v but got: %#v", test.testName, test.mergedTolerations, updatedPodTolerations)
if test.admit {
assert.ElementsMatch(t, updatedPodTolerations, test.mergedTolerations)
}
})
}