Skip to content

Commit 5a50b3f

Browse files
committed
Fix toleration comparison & merging logic
1 parent 3972485 commit 5a50b3f

File tree

4 files changed

+442
-123
lines changed

4 files changed

+442
-123
lines changed

hack/.golint_failures

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ pkg/util/rlimit
244244
pkg/util/selinux
245245
pkg/util/sysctl/testing
246246
pkg/util/taints
247-
pkg/util/tolerations
248247
pkg/version/verflag
249248
pkg/volume
250249
pkg/volume/azure_dd

pkg/util/tolerations/BUILD

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ go_library(
1313
"tolerations.go",
1414
],
1515
importpath = "k8s.io/kubernetes/pkg/util/tolerations",
16-
deps = ["//pkg/apis/core:go_default_library"],
16+
deps = [
17+
"//pkg/apis/core:go_default_library",
18+
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
19+
"//vendor/k8s.io/klog:go_default_library",
20+
],
1721
)
1822

1923
filegroup(
@@ -33,5 +37,12 @@ go_test(
3337
name = "go_default_test",
3438
srcs = ["tolerations_test.go"],
3539
embed = [":go_default_library"],
36-
deps = ["//pkg/apis/core:go_default_library"],
40+
deps = [
41+
"//pkg/apis/core:go_default_library",
42+
"//pkg/apis/core/validation:go_default_library",
43+
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
44+
"//vendor/github.com/stretchr/testify/assert:go_default_library",
45+
"//vendor/github.com/stretchr/testify/require:go_default_library",
46+
"//vendor/k8s.io/utils/pointer:go_default_library",
47+
],
3748
)

pkg/util/tolerations/tolerations.go

Lines changed: 59 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -17,114 +17,91 @@ limitations under the License.
1717
package tolerations
1818

1919
import (
20+
apiequality "k8s.io/apimachinery/pkg/api/equality"
21+
"k8s.io/klog"
2022
api "k8s.io/kubernetes/pkg/apis/core"
2123
)
2224

23-
type key struct {
24-
tolerationKey string
25-
effect api.TaintEffect
26-
}
27-
28-
func convertTolerationToKey(in api.Toleration) key {
29-
return key{in.Key, in.Effect}
30-
}
31-
3225
// VerifyAgainstWhitelist checks if the provided tolerations
3326
// satisfy the provided whitelist and returns true, otherwise returns false
34-
func VerifyAgainstWhitelist(tolerations []api.Toleration, whitelist []api.Toleration) bool {
35-
if len(whitelist) == 0 {
27+
func VerifyAgainstWhitelist(tolerations, whitelist []api.Toleration) bool {
28+
if len(whitelist) == 0 || len(tolerations) == 0 {
3629
return true
3730
}
3831

39-
t := ConvertTolerationToAMap(tolerations)
40-
w := ConvertTolerationToAMap(whitelist)
41-
42-
for k1, v1 := range t {
43-
if v2, ok := w[k1]; !ok || !AreEqual(v1, v2) {
44-
return false
32+
next:
33+
for _, t := range tolerations {
34+
for _, w := range whitelist {
35+
if isSuperset(w, t) {
36+
continue next
37+
}
4538
}
39+
return false
4640
}
41+
4742
return true
4843
}
4944

50-
// IsConflict returns true if the key of two tolerations match
51-
// but one or more other fields differ, otherwise returns false
52-
func IsConflict(first []api.Toleration, second []api.Toleration) bool {
53-
firstMap := ConvertTolerationToAMap(first)
54-
secondMap := ConvertTolerationToAMap(second)
55-
56-
for k1, v1 := range firstMap {
57-
if v2, ok := secondMap[k1]; ok && !AreEqual(v1, v2) {
58-
return true
45+
// MergeTolerations merges two sets of tolerations into one. If one toleration is a superset of
46+
// another, only the superset is kept.
47+
func MergeTolerations(first, second []api.Toleration) []api.Toleration {
48+
all := append(first, second...)
49+
merged := []api.Toleration{}
50+
51+
next:
52+
for i, t := range all {
53+
for _, t2 := range merged {
54+
if isSuperset(t2, t) {
55+
continue next // t is redundant; ignore it
56+
}
5957
}
60-
}
61-
return false
62-
}
63-
64-
// MergeTolerations merges two sets of tolerations into one
65-
// it does not check for conflicts
66-
func MergeTolerations(first []api.Toleration, second []api.Toleration) []api.Toleration {
67-
var mergedTolerations []api.Toleration
68-
mergedTolerations = append(mergedTolerations, second...)
69-
firstMap := ConvertTolerationToAMap(first)
70-
secondMap := ConvertTolerationToAMap(second)
71-
// preserve order of first when merging
72-
for _, v := range first {
73-
k := convertTolerationToKey(v)
74-
// if first contains key conflicts, the last one takes precedence
75-
if _, ok := secondMap[k]; !ok && firstMap[k] == v {
76-
mergedTolerations = append(mergedTolerations, v)
58+
if i+1 < len(all) {
59+
for _, t2 := range all[i+1:] {
60+
// If the tolerations are equal, prefer the first.
61+
if isSuperset(t2, t) && !apiequality.Semantic.DeepEqual(&t, &t2) {
62+
continue next // t is redundant; ignore it
63+
}
64+
}
7765
}
66+
merged = append(merged, t)
7867
}
79-
return mergedTolerations
68+
69+
return merged
8070
}
8171

82-
// EqualTolerations returns true if two sets of tolerations are equal, otherwise false
83-
// it assumes no duplicates in individual set of tolerations
84-
func EqualTolerations(first []api.Toleration, second []api.Toleration) bool {
85-
if len(first) != len(second) {
86-
return false
72+
// isSuperset checks whether ss tolerates a superset of t.
73+
func isSuperset(ss, t api.Toleration) bool {
74+
if apiequality.Semantic.DeepEqual(&t, &ss) {
75+
return true
8776
}
8877

89-
firstMap := ConvertTolerationToAMap(first)
90-
secondMap := ConvertTolerationToAMap(second)
91-
92-
for k1, v1 := range firstMap {
93-
if v2, ok := secondMap[k1]; !ok || !AreEqual(v1, v2) {
94-
return false
95-
}
78+
if t.Key != ss.Key &&
79+
// An empty key with Exists operator means match all keys & values.
80+
(ss.Key != "" || ss.Operator != api.TolerationOpExists) {
81+
return false
9682
}
97-
return true
98-
}
9983

100-
// ConvertTolerationToAMap converts toleration list into a map[string]api.Toleration
101-
func ConvertTolerationToAMap(in []api.Toleration) map[key]api.Toleration {
102-
out := map[key]api.Toleration{}
103-
for _, v := range in {
104-
out[convertTolerationToKey(v)] = v
84+
// An empty effect means match all effects.
85+
if t.Effect != ss.Effect && ss.Effect != "" {
86+
return false
10587
}
106-
return out
107-
}
10888

109-
// AreEqual checks if two provided tolerations are equal or not.
110-
func AreEqual(first, second api.Toleration) bool {
111-
if first.Key == second.Key &&
112-
first.Operator == second.Operator &&
113-
first.Value == second.Value &&
114-
first.Effect == second.Effect &&
115-
AreTolerationSecondsEqual(first.TolerationSeconds, second.TolerationSeconds) {
116-
return true
89+
if ss.Effect == api.TaintEffectNoExecute {
90+
if ss.TolerationSeconds != nil {
91+
if t.TolerationSeconds == nil ||
92+
*t.TolerationSeconds > *ss.TolerationSeconds {
93+
return false
94+
}
95+
}
11796
}
118-
return false
119-
}
12097

121-
// AreTolerationSecondsEqual checks if two provided TolerationSeconds are equal or not.
122-
func AreTolerationSecondsEqual(ts1, ts2 *int64) bool {
123-
if ts1 == ts2 {
124-
return true
125-
}
126-
if ts1 != nil && ts2 != nil && *ts1 == *ts2 {
98+
switch ss.Operator {
99+
case api.TolerationOpEqual, "": // empty operator means Equal
100+
return t.Operator == api.TolerationOpEqual && t.Value == ss.Value
101+
case api.TolerationOpExists:
127102
return true
103+
default:
104+
klog.Errorf("Unknown toleration operator: %s", ss.Operator)
105+
return false
128106
}
129-
return false
130107
}

0 commit comments

Comments
 (0)