...

Source file src/k8s.io/kubectl/pkg/cmd/taint/utils.go

Documentation: k8s.io/kubectl/pkg/cmd/taint

     1  /*
     2  Copyright 2018 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 utilites for working with taints
    18  package taint
    19  
    20  import (
    21  	"fmt"
    22  	"strings"
    23  
    24  	corev1 "k8s.io/api/core/v1"
    25  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    26  	"k8s.io/apimachinery/pkg/util/sets"
    27  	"k8s.io/apimachinery/pkg/util/validation"
    28  )
    29  
    30  // Exported taint constant strings
    31  const (
    32  	MODIFIED  = "modified"
    33  	TAINTED   = "tainted"
    34  	UNTAINTED = "untainted"
    35  )
    36  
    37  // parseTaints takes a spec which is an array and creates slices for new taints to be added, taints to be deleted.
    38  // It also validates the spec. For example, the form `<key>` may be used to remove a taint, but not to add one.
    39  func parseTaints(spec []string) ([]corev1.Taint, []corev1.Taint, error) {
    40  	var taints, taintsToRemove []corev1.Taint
    41  	uniqueTaints := map[corev1.TaintEffect]sets.String{}
    42  
    43  	for _, taintSpec := range spec {
    44  		if strings.HasSuffix(taintSpec, "-") {
    45  			taintToRemove, err := parseTaint(strings.TrimSuffix(taintSpec, "-"))
    46  			if err != nil {
    47  				return nil, nil, err
    48  			}
    49  			taintsToRemove = append(taintsToRemove, corev1.Taint{Key: taintToRemove.Key, Effect: taintToRemove.Effect})
    50  		} else {
    51  			newTaint, err := parseTaint(taintSpec)
    52  			if err != nil {
    53  				return nil, nil, err
    54  			}
    55  			// validate that the taint has an effect, which is required to add the taint
    56  			if len(newTaint.Effect) == 0 {
    57  				return nil, nil, fmt.Errorf("invalid taint spec: %v", taintSpec)
    58  			}
    59  			// validate if taint is unique by <key, effect>
    60  			if len(uniqueTaints[newTaint.Effect]) > 0 && uniqueTaints[newTaint.Effect].Has(newTaint.Key) {
    61  				return nil, nil, fmt.Errorf("duplicated taints with the same key and effect: %v", newTaint)
    62  			}
    63  			// add taint to existingTaints for uniqueness check
    64  			if len(uniqueTaints[newTaint.Effect]) == 0 {
    65  				uniqueTaints[newTaint.Effect] = sets.String{}
    66  			}
    67  			uniqueTaints[newTaint.Effect].Insert(newTaint.Key)
    68  
    69  			taints = append(taints, newTaint)
    70  		}
    71  	}
    72  	return taints, taintsToRemove, nil
    73  }
    74  
    75  // parseTaint parses a taint from a string, whose form must be either
    76  // '<key>=<value>:<effect>', '<key>:<effect>', or '<key>'.
    77  func parseTaint(st string) (corev1.Taint, error) {
    78  	var taint corev1.Taint
    79  
    80  	var key string
    81  	var value string
    82  	var effect corev1.TaintEffect
    83  
    84  	parts := strings.Split(st, ":")
    85  	switch len(parts) {
    86  	case 1:
    87  		key = parts[0]
    88  	case 2:
    89  		effect = corev1.TaintEffect(parts[1])
    90  		if err := validateTaintEffect(effect); err != nil {
    91  			return taint, err
    92  		}
    93  
    94  		partsKV := strings.Split(parts[0], "=")
    95  		if len(partsKV) > 2 {
    96  			return taint, fmt.Errorf("invalid taint spec: %v", st)
    97  		}
    98  		key = partsKV[0]
    99  		if len(partsKV) == 2 {
   100  			value = partsKV[1]
   101  			if errs := validation.IsValidLabelValue(value); len(errs) > 0 {
   102  				return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
   103  			}
   104  		}
   105  	default:
   106  		return taint, fmt.Errorf("invalid taint spec: %v", st)
   107  	}
   108  
   109  	if errs := validation.IsQualifiedName(key); len(errs) > 0 {
   110  		return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
   111  	}
   112  
   113  	taint.Key = key
   114  	taint.Value = value
   115  	taint.Effect = effect
   116  
   117  	return taint, nil
   118  }
   119  
   120  func validateTaintEffect(effect corev1.TaintEffect) error {
   121  	if effect != corev1.TaintEffectNoSchedule && effect != corev1.TaintEffectPreferNoSchedule && effect != corev1.TaintEffectNoExecute {
   122  		return fmt.Errorf("invalid taint effect: %v, unsupported taint effect", effect)
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  // reorganizeTaints returns the updated set of taints, taking into account old taints that were not updated,
   129  // old taints that were updated, old taints that were deleted, and new taints.
   130  func reorganizeTaints(node *corev1.Node, overwrite bool, taintsToAdd []corev1.Taint, taintsToRemove []corev1.Taint) (string, []corev1.Taint, error) {
   131  	newTaints := append([]corev1.Taint{}, taintsToAdd...)
   132  	oldTaints := node.Spec.Taints
   133  	// add taints that already existing but not updated to newTaints
   134  	added := addTaints(oldTaints, &newTaints)
   135  	allErrs, deleted := deleteTaints(taintsToRemove, &newTaints)
   136  	if (added && deleted) || overwrite {
   137  		return MODIFIED, newTaints, utilerrors.NewAggregate(allErrs)
   138  	} else if added {
   139  		return TAINTED, newTaints, utilerrors.NewAggregate(allErrs)
   140  	}
   141  	return UNTAINTED, newTaints, utilerrors.NewAggregate(allErrs)
   142  }
   143  
   144  // deleteTaints deletes the given taints from the node's taintlist.
   145  func deleteTaints(taintsToRemove []corev1.Taint, newTaints *[]corev1.Taint) ([]error, bool) {
   146  	allErrs := []error{}
   147  	var removed bool
   148  	for _, taintToRemove := range taintsToRemove {
   149  		if len(taintToRemove.Effect) > 0 {
   150  			*newTaints, removed = deleteTaint(*newTaints, &taintToRemove)
   151  		} else {
   152  			*newTaints, removed = deleteTaintsByKey(*newTaints, taintToRemove.Key)
   153  		}
   154  		if !removed {
   155  			allErrs = append(allErrs, fmt.Errorf("taint %q not found", taintToRemove.ToString()))
   156  		}
   157  	}
   158  	return allErrs, removed
   159  }
   160  
   161  // addTaints adds the newTaints list to existing ones and updates the newTaints List.
   162  // TODO: This needs a rewrite to take only the new values instead of appended newTaints list to be consistent.
   163  func addTaints(oldTaints []corev1.Taint, newTaints *[]corev1.Taint) bool {
   164  	for _, oldTaint := range oldTaints {
   165  		existsInNew := false
   166  		for _, taint := range *newTaints {
   167  			if taint.MatchTaint(&oldTaint) {
   168  				existsInNew = true
   169  				break
   170  			}
   171  		}
   172  		if !existsInNew {
   173  			*newTaints = append(*newTaints, oldTaint)
   174  		}
   175  	}
   176  	return len(oldTaints) != len(*newTaints)
   177  }
   178  
   179  // checkIfTaintsAlreadyExists checks if the node already has taints that we want to add and returns a string with taint keys.
   180  func checkIfTaintsAlreadyExists(oldTaints []corev1.Taint, taints []corev1.Taint) string {
   181  	var existingTaintList = make([]string, 0)
   182  	for _, taint := range taints {
   183  		for _, oldTaint := range oldTaints {
   184  			if taint.Key == oldTaint.Key && taint.Effect == oldTaint.Effect {
   185  				existingTaintList = append(existingTaintList, taint.Key)
   186  			}
   187  		}
   188  	}
   189  	return strings.Join(existingTaintList, ",")
   190  }
   191  
   192  // deleteTaintsByKey removes all the taints that have the same key to given taintKey
   193  func deleteTaintsByKey(taints []corev1.Taint, taintKey string) ([]corev1.Taint, bool) {
   194  	newTaints := []corev1.Taint{}
   195  	for i := range taints {
   196  		if taintKey == taints[i].Key {
   197  			continue
   198  		}
   199  		newTaints = append(newTaints, taints[i])
   200  	}
   201  	return newTaints, len(taints) != len(newTaints)
   202  }
   203  
   204  // deleteTaint removes all the taints that have the same key and effect to given taintToDelete.
   205  func deleteTaint(taints []corev1.Taint, taintToDelete *corev1.Taint) ([]corev1.Taint, bool) {
   206  	newTaints := []corev1.Taint{}
   207  	for i := range taints {
   208  		if taintToDelete.MatchTaint(&taints[i]) {
   209  			continue
   210  		}
   211  		newTaints = append(newTaints, taints[i])
   212  	}
   213  	return newTaints, len(taints) != len(newTaints)
   214  }
   215  

View as plain text