mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-30 21:30:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			353 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			353 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2016 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| // package taints implements utilities for working with taints
 | |
| package taints
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	"k8s.io/api/core/v1"
 | |
| 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 | |
| 	"k8s.io/apimachinery/pkg/util/sets"
 | |
| 	"k8s.io/apimachinery/pkg/util/validation"
 | |
| 	api "k8s.io/kubernetes/pkg/apis/core"
 | |
| 	"k8s.io/kubernetes/pkg/apis/core/helper"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	MODIFIED  = "modified"
 | |
| 	TAINTED   = "tainted"
 | |
| 	UNTAINTED = "untainted"
 | |
| )
 | |
| 
 | |
| // parseTaint parses a taint from a string, whose form must be either
 | |
| // '<key>=<value>:<effect>', '<key>:<effect>', or '<key>'.
 | |
| func parseTaint(st string) (v1.Taint, error) {
 | |
| 	var taint v1.Taint
 | |
| 
 | |
| 	var key string
 | |
| 	var value string
 | |
| 	var effect v1.TaintEffect
 | |
| 
 | |
| 	parts := strings.Split(st, ":")
 | |
| 	switch len(parts) {
 | |
| 	case 1:
 | |
| 		key = parts[0]
 | |
| 	case 2:
 | |
| 		effect = v1.TaintEffect(parts[1])
 | |
| 		if err := validateTaintEffect(effect); err != nil {
 | |
| 			return taint, err
 | |
| 		}
 | |
| 
 | |
| 		partsKV := strings.Split(parts[0], "=")
 | |
| 		if len(partsKV) > 2 {
 | |
| 			return taint, fmt.Errorf("invalid taint spec: %v", st)
 | |
| 		}
 | |
| 		key = partsKV[0]
 | |
| 		if len(partsKV) == 2 {
 | |
| 			value = partsKV[1]
 | |
| 			if errs := validation.IsValidLabelValue(value); len(errs) > 0 {
 | |
| 				return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
 | |
| 			}
 | |
| 		}
 | |
| 	default:
 | |
| 		return taint, fmt.Errorf("invalid taint spec: %v", st)
 | |
| 	}
 | |
| 
 | |
| 	if errs := validation.IsQualifiedName(key); len(errs) > 0 {
 | |
| 		return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
 | |
| 	}
 | |
| 
 | |
| 	taint.Key = key
 | |
| 	taint.Value = value
 | |
| 	taint.Effect = effect
 | |
| 
 | |
| 	return taint, nil
 | |
| }
 | |
| 
 | |
| func validateTaintEffect(effect v1.TaintEffect) error {
 | |
| 	if effect != v1.TaintEffectNoSchedule && effect != v1.TaintEffectPreferNoSchedule && effect != v1.TaintEffectNoExecute {
 | |
| 		return fmt.Errorf("invalid taint effect: %v, unsupported taint effect", effect)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // NewTaintsVar wraps []api.Taint in a struct that implements flag.Value to allow taints to be
 | |
| // bound to command line flags.
 | |
| func NewTaintsVar(ptr *[]api.Taint) taintsVar {
 | |
| 	return taintsVar{
 | |
| 		ptr: ptr,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type taintsVar struct {
 | |
| 	ptr *[]api.Taint
 | |
| }
 | |
| 
 | |
| func (t taintsVar) Set(s string) error {
 | |
| 	if len(s) == 0 {
 | |
| 		*t.ptr = nil
 | |
| 		return nil
 | |
| 	}
 | |
| 	sts := strings.Split(s, ",")
 | |
| 	var taints []api.Taint
 | |
| 	for _, st := range sts {
 | |
| 		taint, err := parseTaint(st)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		taints = append(taints, api.Taint{Key: taint.Key, Value: taint.Value, Effect: api.TaintEffect(taint.Effect)})
 | |
| 	}
 | |
| 	*t.ptr = taints
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (t taintsVar) String() string {
 | |
| 	if len(*t.ptr) == 0 {
 | |
| 		return ""
 | |
| 	}
 | |
| 	var taints []string
 | |
| 	for _, taint := range *t.ptr {
 | |
| 		taints = append(taints, fmt.Sprintf("%s=%s:%s", taint.Key, taint.Value, taint.Effect))
 | |
| 	}
 | |
| 	return strings.Join(taints, ",")
 | |
| }
 | |
| 
 | |
| func (t taintsVar) Type() string {
 | |
| 	return "[]api.Taint"
 | |
| }
 | |
| 
 | |
| // ParseTaints takes a spec which is an array and creates slices for new taints to be added, taints to be deleted.
 | |
| // It also validates the spec. For example, the form `<key>` may be used to remove a taint, but not to add one.
 | |
| func ParseTaints(spec []string) ([]v1.Taint, []v1.Taint, error) {
 | |
| 	var taints, taintsToRemove []v1.Taint
 | |
| 	uniqueTaints := map[v1.TaintEffect]sets.String{}
 | |
| 
 | |
| 	for _, taintSpec := range spec {
 | |
| 		if strings.HasSuffix(taintSpec, "-") {
 | |
| 			taintToRemove, err := parseTaint(strings.TrimSuffix(taintSpec, "-"))
 | |
| 			if err != nil {
 | |
| 				return nil, nil, err
 | |
| 			}
 | |
| 			taintsToRemove = append(taintsToRemove, v1.Taint{Key: taintToRemove.Key, Effect: taintToRemove.Effect})
 | |
| 		} else {
 | |
| 			newTaint, err := parseTaint(taintSpec)
 | |
| 			if err != nil {
 | |
| 				return nil, nil, err
 | |
| 			}
 | |
| 			// validate that the taint has an effect, which is required to add the taint
 | |
| 			if len(newTaint.Effect) == 0 {
 | |
| 				return nil, nil, fmt.Errorf("invalid taint spec: %v", taintSpec)
 | |
| 			}
 | |
| 			// validate if taint is unique by <key, effect>
 | |
| 			if len(uniqueTaints[newTaint.Effect]) > 0 && uniqueTaints[newTaint.Effect].Has(newTaint.Key) {
 | |
| 				return nil, nil, fmt.Errorf("duplicated taints with the same key and effect: %v", newTaint)
 | |
| 			}
 | |
| 			// add taint to existingTaints for uniqueness check
 | |
| 			if len(uniqueTaints[newTaint.Effect]) == 0 {
 | |
| 				uniqueTaints[newTaint.Effect] = sets.String{}
 | |
| 			}
 | |
| 			uniqueTaints[newTaint.Effect].Insert(newTaint.Key)
 | |
| 
 | |
| 			taints = append(taints, newTaint)
 | |
| 		}
 | |
| 	}
 | |
| 	return taints, taintsToRemove, nil
 | |
| }
 | |
| 
 | |
| // ReorganizeTaints returns the updated set of taints, taking into account old taints that were not updated,
 | |
| // old taints that were updated, old taints that were deleted, and new taints.
 | |
| func ReorganizeTaints(node *v1.Node, overwrite bool, taintsToAdd []v1.Taint, taintsToRemove []v1.Taint) (string, []v1.Taint, error) {
 | |
| 	newTaints := append([]v1.Taint{}, taintsToAdd...)
 | |
| 	oldTaints := node.Spec.Taints
 | |
| 	// add taints that already existing but not updated to newTaints
 | |
| 	added := addTaints(oldTaints, &newTaints)
 | |
| 	allErrs, deleted := deleteTaints(taintsToRemove, &newTaints)
 | |
| 	if (added && deleted) || overwrite {
 | |
| 		return MODIFIED, newTaints, utilerrors.NewAggregate(allErrs)
 | |
| 	} else if added {
 | |
| 		return TAINTED, newTaints, utilerrors.NewAggregate(allErrs)
 | |
| 	}
 | |
| 	return UNTAINTED, newTaints, utilerrors.NewAggregate(allErrs)
 | |
| }
 | |
| 
 | |
| // deleteTaints deletes the given taints from the node's taintlist.
 | |
| func deleteTaints(taintsToRemove []v1.Taint, newTaints *[]v1.Taint) ([]error, bool) {
 | |
| 	allErrs := []error{}
 | |
| 	var removed bool
 | |
| 	for _, taintToRemove := range taintsToRemove {
 | |
| 		removed = false
 | |
| 		if len(taintToRemove.Effect) > 0 {
 | |
| 			*newTaints, removed = DeleteTaint(*newTaints, &taintToRemove)
 | |
| 		} else {
 | |
| 			*newTaints, removed = DeleteTaintsByKey(*newTaints, taintToRemove.Key)
 | |
| 		}
 | |
| 		if !removed {
 | |
| 			allErrs = append(allErrs, fmt.Errorf("taint %q not found", taintToRemove.ToString()))
 | |
| 		}
 | |
| 	}
 | |
| 	return allErrs, removed
 | |
| }
 | |
| 
 | |
| // addTaints adds the newTaints list to existing ones and updates the newTaints List.
 | |
| // TODO: This needs a rewrite to take only the new values instead of appended newTaints list to be consistent.
 | |
| func addTaints(oldTaints []v1.Taint, newTaints *[]v1.Taint) bool {
 | |
| 	for _, oldTaint := range oldTaints {
 | |
| 		existsInNew := false
 | |
| 		for _, taint := range *newTaints {
 | |
| 			if taint.MatchTaint(&oldTaint) {
 | |
| 				existsInNew = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if !existsInNew {
 | |
| 			*newTaints = append(*newTaints, oldTaint)
 | |
| 		}
 | |
| 	}
 | |
| 	return len(oldTaints) != len(*newTaints)
 | |
| }
 | |
| 
 | |
| // CheckIfTaintsAlreadyExists checks if the node already has taints that we want to add and returns a string with taint keys.
 | |
| func CheckIfTaintsAlreadyExists(oldTaints []v1.Taint, taints []v1.Taint) string {
 | |
| 	var existingTaintList = make([]string, 0)
 | |
| 	for _, taint := range taints {
 | |
| 		for _, oldTaint := range oldTaints {
 | |
| 			if taint.Key == oldTaint.Key && taint.Effect == oldTaint.Effect {
 | |
| 				existingTaintList = append(existingTaintList, taint.Key)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return strings.Join(existingTaintList, ",")
 | |
| }
 | |
| 
 | |
| // DeleteTaintsByKey removes all the taints that have the same key to given taintKey
 | |
| func DeleteTaintsByKey(taints []v1.Taint, taintKey string) ([]v1.Taint, bool) {
 | |
| 	newTaints := []v1.Taint{}
 | |
| 	deleted := false
 | |
| 	for i := range taints {
 | |
| 		if taintKey == taints[i].Key {
 | |
| 			deleted = true
 | |
| 			continue
 | |
| 		}
 | |
| 		newTaints = append(newTaints, taints[i])
 | |
| 	}
 | |
| 	return newTaints, deleted
 | |
| }
 | |
| 
 | |
| // DeleteTaint removes all the taints that have the same key and effect to given taintToDelete.
 | |
| func DeleteTaint(taints []v1.Taint, taintToDelete *v1.Taint) ([]v1.Taint, bool) {
 | |
| 	newTaints := []v1.Taint{}
 | |
| 	deleted := false
 | |
| 	for i := range taints {
 | |
| 		if taintToDelete.MatchTaint(&taints[i]) {
 | |
| 			deleted = true
 | |
| 			continue
 | |
| 		}
 | |
| 		newTaints = append(newTaints, taints[i])
 | |
| 	}
 | |
| 	return newTaints, deleted
 | |
| }
 | |
| 
 | |
| // RemoveTaint tries to remove a taint from annotations list. Returns a new copy of updated Node and true if something was updated
 | |
| // false otherwise.
 | |
| func RemoveTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
 | |
| 	newNode := node.DeepCopy()
 | |
| 	nodeTaints := newNode.Spec.Taints
 | |
| 	if len(nodeTaints) == 0 {
 | |
| 		return newNode, false, nil
 | |
| 	}
 | |
| 
 | |
| 	if !TaintExists(nodeTaints, taint) {
 | |
| 		return newNode, false, nil
 | |
| 	}
 | |
| 
 | |
| 	newTaints, _ := DeleteTaint(nodeTaints, taint)
 | |
| 	newNode.Spec.Taints = newTaints
 | |
| 	return newNode, true, nil
 | |
| }
 | |
| 
 | |
| // AddOrUpdateTaint tries to add a taint to annotations list. Returns a new copy of updated Node and true if something was updated
 | |
| // false otherwise.
 | |
| func AddOrUpdateTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
 | |
| 	newNode := node.DeepCopy()
 | |
| 	nodeTaints := newNode.Spec.Taints
 | |
| 
 | |
| 	var newTaints []v1.Taint
 | |
| 	updated := false
 | |
| 	for i := range nodeTaints {
 | |
| 		if taint.MatchTaint(&nodeTaints[i]) {
 | |
| 			if helper.Semantic.DeepEqual(*taint, nodeTaints[i]) {
 | |
| 				return newNode, false, nil
 | |
| 			}
 | |
| 			newTaints = append(newTaints, *taint)
 | |
| 			updated = true
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		newTaints = append(newTaints, nodeTaints[i])
 | |
| 	}
 | |
| 
 | |
| 	if !updated {
 | |
| 		newTaints = append(newTaints, *taint)
 | |
| 	}
 | |
| 
 | |
| 	newNode.Spec.Taints = newTaints
 | |
| 	return newNode, true, nil
 | |
| }
 | |
| 
 | |
| // TaintExists checks if the given taint exists in list of taints. Returns true if exists false otherwise.
 | |
| func TaintExists(taints []v1.Taint, taintToFind *v1.Taint) bool {
 | |
| 	for _, taint := range taints {
 | |
| 		if taint.MatchTaint(taintToFind) {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func TaintSetDiff(t1, t2 []v1.Taint) (taintsToAdd []*v1.Taint, taintsToRemove []*v1.Taint) {
 | |
| 	for _, taint := range t1 {
 | |
| 		if !TaintExists(t2, &taint) {
 | |
| 			t := taint
 | |
| 			taintsToAdd = append(taintsToAdd, &t)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, taint := range t2 {
 | |
| 		if !TaintExists(t1, &taint) {
 | |
| 			t := taint
 | |
| 			taintsToRemove = append(taintsToRemove, &t)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func TaintSetFilter(taints []v1.Taint, fn func(*v1.Taint) bool) []v1.Taint {
 | |
| 	res := []v1.Taint{}
 | |
| 
 | |
| 	for _, taint := range taints {
 | |
| 		if fn(&taint) {
 | |
| 			res = append(res, taint)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return res
 | |
| }
 |