2019-05-31 09:45:11 +00:00
|
|
|
/*
|
|
|
|
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"
|
|
|
|
|
2021-12-08 13:50:47 +00:00
|
|
|
v1 "k8s.io/api/core/v1"
|
2019-05-31 09:45:11 +00:00
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
|
|
"k8s.io/apimachinery/pkg/util/validation"
|
|
|
|
"k8s.io/kubernetes/pkg/apis/core/helper"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
MODIFIED = "modified"
|
|
|
|
TAINTED = "tainted"
|
|
|
|
UNTAINTED = "untainted"
|
|
|
|
)
|
|
|
|
|
2019-06-24 09:08:09 +00:00
|
|
|
// parseTaint parses a taint from a string, whose form must be either
|
|
|
|
// '<key>=<value>:<effect>', '<key>:<effect>', or '<key>'.
|
2019-05-31 09:45:11 +00:00
|
|
|
func parseTaint(st string) (v1.Taint, error) {
|
|
|
|
var taint v1.Taint
|
|
|
|
|
2019-06-24 09:08:09 +00:00
|
|
|
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
|
|
|
|
}
|
2019-05-31 09:45:11 +00:00
|
|
|
|
2019-06-24 09:08:09 +00:00
|
|
|
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)
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
|
2019-06-24 09:08:09 +00:00
|
|
|
if errs := validation.IsQualifiedName(key); len(errs) > 0 {
|
|
|
|
return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
|
2019-06-24 09:08:09 +00:00
|
|
|
taint.Key = key
|
|
|
|
taint.Value = value
|
2019-05-31 09:45:11 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseTaints takes a spec which is an array and creates slices for new taints to be added, taints to be deleted.
|
2019-06-24 09:08:09 +00:00
|
|
|
// It also validates the spec. For example, the form `<key>` may be used to remove a taint, but not to add one.
|
2019-05-31 09:45:11 +00:00
|
|
|
func ParseTaints(spec []string) ([]v1.Taint, []v1.Taint, error) {
|
|
|
|
var taints, taintsToRemove []v1.Taint
|
|
|
|
uniqueTaints := map[v1.TaintEffect]sets.String{}
|
|
|
|
|
|
|
|
for _, taintSpec := range spec {
|
2019-06-24 09:08:09 +00:00
|
|
|
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 {
|
2019-05-31 09:45:11 +00:00
|
|
|
newTaint, err := parseTaint(taintSpec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2019-06-24 09:08:09 +00:00
|
|
|
// 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)
|
|
|
|
}
|
2019-05-31 09:45:11 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-05-05 02:47:06 +00:00
|
|
|
// TaintKeyExists checks if the given taint key exists in list of taints. Returns true if exists false otherwise.
|
|
|
|
func TaintKeyExists(taints []v1.Taint, taintKeyToMatch string) bool {
|
|
|
|
for _, taint := range taints {
|
|
|
|
if taint.Key == taintKeyToMatch {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-02-01 17:06:36 +00:00
|
|
|
// TaintSetDiff finds the difference between two taint slices and
|
|
|
|
// returns all new and removed elements of the new slice relative to the old slice.
|
|
|
|
// for example:
|
|
|
|
// input: taintsNew=[a b] taintsOld=[a c]
|
|
|
|
// output: taintsToAdd=[b] taintsToRemove=[c]
|
|
|
|
func TaintSetDiff(taintsNew, taintsOld []v1.Taint) (taintsToAdd []*v1.Taint, taintsToRemove []*v1.Taint) {
|
|
|
|
for _, taint := range taintsNew {
|
|
|
|
if !TaintExists(taintsOld, &taint) {
|
2019-05-31 09:45:11 +00:00
|
|
|
t := taint
|
|
|
|
taintsToAdd = append(taintsToAdd, &t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-01 17:06:36 +00:00
|
|
|
for _, taint := range taintsOld {
|
|
|
|
if !TaintExists(taintsNew, &taint) {
|
2019-05-31 09:45:11 +00:00
|
|
|
t := taint
|
|
|
|
taintsToRemove = append(taintsToRemove, &t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-01 17:06:36 +00:00
|
|
|
// TaintSetFilter filters from the taint slice according to the passed fn function to get the filtered taint slice.
|
2019-05-31 09:45:11 +00:00
|
|
|
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
|
|
|
|
}
|
2021-12-08 13:50:47 +00:00
|
|
|
|
|
|
|
// CheckTaintValidation checks if the given taint is valid.
|
|
|
|
// Returns error if the given taint is invalid.
|
|
|
|
func CheckTaintValidation(taint v1.Taint) error {
|
|
|
|
if errs := validation.IsQualifiedName(taint.Key); len(errs) > 0 {
|
|
|
|
return fmt.Errorf("invalid taint key: %s", strings.Join(errs, "; "))
|
|
|
|
}
|
|
|
|
if taint.Value != "" {
|
|
|
|
if errs := validation.IsValidLabelValue(taint.Value); len(errs) > 0 {
|
|
|
|
return fmt.Errorf("invalid taint value: %s", strings.Join(errs, "; "))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if taint.Effect != "" {
|
|
|
|
if err := validateTaintEffect(taint.Effect); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|