123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- /*
- 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
- }
|