k8s.io/kubernetes@v1.29.3/pkg/util/taints/taints.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // package taints implements utilities for working with taints 18 package taints 19 20 import ( 21 "fmt" 22 "strings" 23 24 v1 "k8s.io/api/core/v1" 25 "k8s.io/apimachinery/pkg/util/sets" 26 "k8s.io/apimachinery/pkg/util/validation" 27 "k8s.io/kubernetes/pkg/apis/core/helper" 28 ) 29 30 const ( 31 MODIFIED = "modified" 32 TAINTED = "tainted" 33 UNTAINTED = "untainted" 34 ) 35 36 // parseTaint parses a taint from a string, whose form must be either 37 // '<key>=<value>:<effect>', '<key>:<effect>', or '<key>'. 38 func parseTaint(st string) (v1.Taint, error) { 39 var taint v1.Taint 40 41 var key string 42 var value string 43 var effect v1.TaintEffect 44 45 parts := strings.Split(st, ":") 46 switch len(parts) { 47 case 1: 48 key = parts[0] 49 case 2: 50 effect = v1.TaintEffect(parts[1]) 51 if err := validateTaintEffect(effect); err != nil { 52 return taint, err 53 } 54 55 partsKV := strings.Split(parts[0], "=") 56 if len(partsKV) > 2 { 57 return taint, fmt.Errorf("invalid taint spec: %v", st) 58 } 59 key = partsKV[0] 60 if len(partsKV) == 2 { 61 value = partsKV[1] 62 if errs := validation.IsValidLabelValue(value); len(errs) > 0 { 63 return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; ")) 64 } 65 } 66 default: 67 return taint, fmt.Errorf("invalid taint spec: %v", st) 68 } 69 70 if errs := validation.IsQualifiedName(key); len(errs) > 0 { 71 return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; ")) 72 } 73 74 taint.Key = key 75 taint.Value = value 76 taint.Effect = effect 77 78 return taint, nil 79 } 80 81 func validateTaintEffect(effect v1.TaintEffect) error { 82 if effect != v1.TaintEffectNoSchedule && effect != v1.TaintEffectPreferNoSchedule && effect != v1.TaintEffectNoExecute { 83 return fmt.Errorf("invalid taint effect: %v, unsupported taint effect", effect) 84 } 85 86 return nil 87 } 88 89 // ParseTaints takes a spec which is an array and creates slices for new taints to be added, taints to be deleted. 90 // It also validates the spec. For example, the form `<key>` may be used to remove a taint, but not to add one. 91 func ParseTaints(spec []string) ([]v1.Taint, []v1.Taint, error) { 92 var taints, taintsToRemove []v1.Taint 93 uniqueTaints := map[v1.TaintEffect]sets.String{} 94 95 for _, taintSpec := range spec { 96 if strings.HasSuffix(taintSpec, "-") { 97 taintToRemove, err := parseTaint(strings.TrimSuffix(taintSpec, "-")) 98 if err != nil { 99 return nil, nil, err 100 } 101 taintsToRemove = append(taintsToRemove, v1.Taint{Key: taintToRemove.Key, Effect: taintToRemove.Effect}) 102 } else { 103 newTaint, err := parseTaint(taintSpec) 104 if err != nil { 105 return nil, nil, err 106 } 107 // validate that the taint has an effect, which is required to add the taint 108 if len(newTaint.Effect) == 0 { 109 return nil, nil, fmt.Errorf("invalid taint spec: %v", taintSpec) 110 } 111 // validate if taint is unique by <key, effect> 112 if len(uniqueTaints[newTaint.Effect]) > 0 && uniqueTaints[newTaint.Effect].Has(newTaint.Key) { 113 return nil, nil, fmt.Errorf("duplicated taints with the same key and effect: %v", newTaint) 114 } 115 // add taint to existingTaints for uniqueness check 116 if len(uniqueTaints[newTaint.Effect]) == 0 { 117 uniqueTaints[newTaint.Effect] = sets.String{} 118 } 119 uniqueTaints[newTaint.Effect].Insert(newTaint.Key) 120 121 taints = append(taints, newTaint) 122 } 123 } 124 return taints, taintsToRemove, nil 125 } 126 127 // CheckIfTaintsAlreadyExists checks if the node already has taints that we want to add and returns a string with taint keys. 128 func CheckIfTaintsAlreadyExists(oldTaints []v1.Taint, taints []v1.Taint) string { 129 var existingTaintList = make([]string, 0) 130 for _, taint := range taints { 131 for _, oldTaint := range oldTaints { 132 if taint.Key == oldTaint.Key && taint.Effect == oldTaint.Effect { 133 existingTaintList = append(existingTaintList, taint.Key) 134 } 135 } 136 } 137 return strings.Join(existingTaintList, ",") 138 } 139 140 // DeleteTaintsByKey removes all the taints that have the same key to given taintKey 141 func DeleteTaintsByKey(taints []v1.Taint, taintKey string) ([]v1.Taint, bool) { 142 newTaints := []v1.Taint{} 143 deleted := false 144 for i := range taints { 145 if taintKey == taints[i].Key { 146 deleted = true 147 continue 148 } 149 newTaints = append(newTaints, taints[i]) 150 } 151 return newTaints, deleted 152 } 153 154 // DeleteTaint removes all the taints that have the same key and effect to given taintToDelete. 155 func DeleteTaint(taints []v1.Taint, taintToDelete *v1.Taint) ([]v1.Taint, bool) { 156 newTaints := []v1.Taint{} 157 deleted := false 158 for i := range taints { 159 if taintToDelete.MatchTaint(&taints[i]) { 160 deleted = true 161 continue 162 } 163 newTaints = append(newTaints, taints[i]) 164 } 165 return newTaints, deleted 166 } 167 168 // RemoveTaint tries to remove a taint from annotations list. Returns a new copy of updated Node and true if something was updated 169 // false otherwise. 170 func RemoveTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) { 171 newNode := node.DeepCopy() 172 nodeTaints := newNode.Spec.Taints 173 if len(nodeTaints) == 0 { 174 return newNode, false, nil 175 } 176 177 if !TaintExists(nodeTaints, taint) { 178 return newNode, false, nil 179 } 180 181 newTaints, _ := DeleteTaint(nodeTaints, taint) 182 newNode.Spec.Taints = newTaints 183 return newNode, true, nil 184 } 185 186 // AddOrUpdateTaint tries to add a taint to annotations list. Returns a new copy of updated Node and true if something was updated 187 // false otherwise. 188 func AddOrUpdateTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) { 189 newNode := node.DeepCopy() 190 nodeTaints := newNode.Spec.Taints 191 192 var newTaints []v1.Taint 193 updated := false 194 for i := range nodeTaints { 195 if taint.MatchTaint(&nodeTaints[i]) { 196 if helper.Semantic.DeepEqual(*taint, nodeTaints[i]) { 197 return newNode, false, nil 198 } 199 newTaints = append(newTaints, *taint) 200 updated = true 201 continue 202 } 203 204 newTaints = append(newTaints, nodeTaints[i]) 205 } 206 207 if !updated { 208 newTaints = append(newTaints, *taint) 209 } 210 211 newNode.Spec.Taints = newTaints 212 return newNode, true, nil 213 } 214 215 // TaintExists checks if the given taint exists in list of taints. Returns true if exists false otherwise. 216 func TaintExists(taints []v1.Taint, taintToFind *v1.Taint) bool { 217 for _, taint := range taints { 218 if taint.MatchTaint(taintToFind) { 219 return true 220 } 221 } 222 return false 223 } 224 225 // TaintKeyExists checks if the given taint key exists in list of taints. Returns true if exists false otherwise. 226 func TaintKeyExists(taints []v1.Taint, taintKeyToMatch string) bool { 227 for _, taint := range taints { 228 if taint.Key == taintKeyToMatch { 229 return true 230 } 231 } 232 return false 233 } 234 235 // TaintSetDiff finds the difference between two taint slices and 236 // returns all new and removed elements of the new slice relative to the old slice. 237 // for example: 238 // input: taintsNew=[a b] taintsOld=[a c] 239 // output: taintsToAdd=[b] taintsToRemove=[c] 240 func TaintSetDiff(taintsNew, taintsOld []v1.Taint) (taintsToAdd []*v1.Taint, taintsToRemove []*v1.Taint) { 241 for _, taint := range taintsNew { 242 if !TaintExists(taintsOld, &taint) { 243 t := taint 244 taintsToAdd = append(taintsToAdd, &t) 245 } 246 } 247 248 for _, taint := range taintsOld { 249 if !TaintExists(taintsNew, &taint) { 250 t := taint 251 taintsToRemove = append(taintsToRemove, &t) 252 } 253 } 254 255 return 256 } 257 258 // TaintSetFilter filters from the taint slice according to the passed fn function to get the filtered taint slice. 259 func TaintSetFilter(taints []v1.Taint, fn func(*v1.Taint) bool) []v1.Taint { 260 res := []v1.Taint{} 261 262 for _, taint := range taints { 263 if fn(&taint) { 264 res = append(res, taint) 265 } 266 } 267 268 return res 269 } 270 271 // CheckTaintValidation checks if the given taint is valid. 272 // Returns error if the given taint is invalid. 273 func CheckTaintValidation(taint v1.Taint) error { 274 if errs := validation.IsQualifiedName(taint.Key); len(errs) > 0 { 275 return fmt.Errorf("invalid taint key: %s", strings.Join(errs, "; ")) 276 } 277 if taint.Value != "" { 278 if errs := validation.IsValidLabelValue(taint.Value); len(errs) > 0 { 279 return fmt.Errorf("invalid taint value: %s", strings.Join(errs, "; ")) 280 } 281 } 282 if taint.Effect != "" { 283 if err := validateTaintEffect(taint.Effect); err != nil { 284 return err 285 } 286 } 287 288 return nil 289 }