...

Source file src/k8s.io/kubernetes/pkg/util/taints/taints.go

Documentation: k8s.io/kubernetes/pkg/util/taints

     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  }
   290  

View as plain text