diff --git a/pkg/api/v1/helper/helpers.go b/pkg/api/v1/helper/helpers.go index f2b2b7c4a0..872ed6b0a1 100644 --- a/pkg/api/v1/helper/helpers.go +++ b/pkg/api/v1/helper/helpers.go @@ -21,11 +21,9 @@ import ( "fmt" "strings" + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" - - "k8s.io/api/core/v1" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/helper" ) @@ -269,34 +267,6 @@ func TolerationsTolerateTaintsWithFilter(tolerations []v1.Toleration, taints []v return true } -// 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 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 -} - // Returns true and list of Tolerations matching all Taints if all are tolerated, or false otherwise. func GetMatchingTolerations(taints []v1.Taint, tolerations []v1.Toleration) (bool, []v1.Toleration) { if len(taints) == 0 { @@ -381,70 +351,6 @@ func PodAnnotationsFromSysctls(sysctls []v1.Sysctl) string { return strings.Join(kvs, ",") } -// 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) { - objCopy, err := api.Scheme.DeepCopy(node) - if err != nil { - return nil, false, err - } - newNode := objCopy.(*v1.Node) - 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 -} - -func TaintExists(taints []v1.Taint, taintToFind *v1.Taint) bool { - for _, taint := range taints { - if taint.MatchTaint(taintToFind) { - return true - } - } - return false -} - -// 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) { - objCopy, err := api.Scheme.DeepCopy(node) - if err != nil { - return nil, false, err - } - newNode := objCopy.(*v1.Node) - 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 -} - // GetPersistentVolumeClass returns StorageClassName. func GetPersistentVolumeClass(volume *v1.PersistentVolume) string { // Use beta annotation first diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index 4556a01d03..fc7fb8c66e 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -43,12 +43,12 @@ import ( "k8s.io/client-go/tools/record" "k8s.io/client-go/util/integer" _ "k8s.io/kubernetes/pkg/api/install" - v1helper "k8s.io/kubernetes/pkg/api/v1/helper" podutil "k8s.io/kubernetes/pkg/api/v1/pod" "k8s.io/kubernetes/pkg/api/v1/ref" "k8s.io/kubernetes/pkg/api/validation" clientretry "k8s.io/kubernetes/pkg/client/retry" hashutil "k8s.io/kubernetes/pkg/util/hash" + taintutils "k8s.io/kubernetes/pkg/util/taints" "github.com/golang/glog" ) @@ -901,7 +901,7 @@ func AddOrUpdateTaintOnNode(c clientset.Interface, nodeName string, taint *v1.Ta if err != nil { return err } - newNode, ok, err := v1helper.AddOrUpdateTaint(oldNode, taint) + newNode, ok, err := taintutils.AddOrUpdateTaint(oldNode, taint) if err != nil { return fmt.Errorf("Failed to update taint annotation!") } @@ -945,7 +945,7 @@ func RemoveTaintOffNode(c clientset.Interface, nodeName string, taint *v1.Taint, if err != nil { return err } - newNode, ok, err := v1helper.RemoveTaint(oldNode, taint) + newNode, ok, err := taintutils.RemoveTaint(oldNode, taint) if err != nil { return fmt.Errorf("Failed to update taint annotation!") } diff --git a/pkg/controller/node/nodecontroller.go b/pkg/controller/node/nodecontroller.go index a473976331..f5737a65e5 100644 --- a/pkg/controller/node/nodecontroller.go +++ b/pkg/controller/node/nodecontroller.go @@ -45,13 +45,13 @@ import ( clientset "k8s.io/client-go/kubernetes" corelisters "k8s.io/client-go/listers/core/v1" extensionslisters "k8s.io/client-go/listers/extensions/v1beta1" - v1helper "k8s.io/kubernetes/pkg/api/v1/helper" v1node "k8s.io/kubernetes/pkg/api/v1/node" "k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/util/metrics" utilnode "k8s.io/kubernetes/pkg/util/node" "k8s.io/kubernetes/pkg/util/system" + taintutils "k8s.io/kubernetes/pkg/util/taints" utilversion "k8s.io/kubernetes/pkg/util/version" "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm" @@ -604,7 +604,7 @@ func (nc *NodeController) monitorNodeStatus() error { if observedReadyCondition.Status == v1.ConditionFalse { if nc.useTaintBasedEvictions { // We want to update the taint straight away if Node is already tainted with the UnreachableTaint - if v1helper.TaintExists(node.Spec.Taints, UnreachableTaintTemplate) { + if taintutils.TaintExists(node.Spec.Taints, UnreachableTaintTemplate) { taintToAdd := *NotReadyTaintTemplate if !swapNodeControllerTaint(nc.kubeClient, &taintToAdd, UnreachableTaintTemplate, node) { glog.Errorf("Failed to instantly swap UnreachableTaint to NotReadyTaint. Will try again in the next cycle.") @@ -631,7 +631,7 @@ func (nc *NodeController) monitorNodeStatus() error { if observedReadyCondition.Status == v1.ConditionUnknown { if nc.useTaintBasedEvictions { // We want to update the taint straight away if Node is already tainted with the UnreachableTaint - if v1helper.TaintExists(node.Spec.Taints, NotReadyTaintTemplate) { + if taintutils.TaintExists(node.Spec.Taints, NotReadyTaintTemplate) { taintToAdd := *UnreachableTaintTemplate if !swapNodeControllerTaint(nc.kubeClient, &taintToAdd, NotReadyTaintTemplate, node) { glog.Errorf("Failed to instantly swap UnreachableTaint to NotReadyTaint. Will try again in the next cycle.") diff --git a/pkg/controller/node/nodecontroller_test.go b/pkg/controller/node/nodecontroller_test.go index e3c040efd4..5c5525302c 100644 --- a/pkg/controller/node/nodecontroller_test.go +++ b/pkg/controller/node/nodecontroller_test.go @@ -36,13 +36,13 @@ import ( clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" testcore "k8s.io/client-go/testing" - v1helper "k8s.io/kubernetes/pkg/api/v1/helper" "k8s.io/kubernetes/pkg/cloudprovider" fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/node/testutil" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" "k8s.io/kubernetes/pkg/util/node" + taintutils "k8s.io/kubernetes/pkg/util/taints" ) const ( @@ -1326,7 +1326,6 @@ func TestMonitorNodeStatusEvictPodsWithDisruption(t *testing.T) { break } } - if item.expectedEvictPods != podEvicted { t.Errorf("%v: expected pod eviction: %+v, got %+v", item.description, item.expectedEvictPods, podEvicted) } @@ -2019,7 +2018,7 @@ func TestSwapUnreachableNotReadyTaints(t *testing.T) { return } - if originalTaint != nil && !v1helper.TaintExists(node0.Spec.Taints, originalTaint) { + if originalTaint != nil && !taintutils.TaintExists(node0.Spec.Taints, originalTaint) { t.Errorf("Can't find taint %v in %v", originalTaint, node0.Spec.Taints) } @@ -2052,7 +2051,7 @@ func TestSwapUnreachableNotReadyTaints(t *testing.T) { return } if updatedTaint != nil { - if !v1helper.TaintExists(node0.Spec.Taints, updatedTaint) { + if !taintutils.TaintExists(node0.Spec.Taints, updatedTaint) { t.Errorf("Can't find taint %v in %v", updatedTaint, node0.Spec.Taints) } } diff --git a/pkg/kubectl/cmd/taint.go b/pkg/kubectl/cmd/taint.go index ca9c054771..73e2c5afe2 100644 --- a/pkg/kubectl/cmd/taint.go +++ b/pkg/kubectl/cmd/taint.go @@ -28,23 +28,14 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/validation" - v1helper "k8s.io/kubernetes/pkg/api/v1/helper" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/util/i18n" - utiltaints "k8s.io/kubernetes/pkg/util/taints" -) - -const ( - MODIFIED = "modified" - TAINTED = "tainted" - UNTAINTED = "untainted" + taintutils "k8s.io/kubernetes/pkg/util/taints" ) // TaintOptions have the data required to perform the taint operation @@ -121,95 +112,6 @@ func NewCmdTaint(f cmdutil.Factory, out io.Writer) *cobra.Command { return cmd } -// 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 = v1helper.DeleteTaint(*newTaints, &taintToRemove) - } else { - *newTaints, removed = v1helper.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) -} - -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.Contains(taintSpec, "=") && strings.Contains(taintSpec, ":") { - newTaint, err := utiltaints.ParseTaint(taintSpec) - if err != nil { - return nil, nil, err - } - // validate if taint is unique by - 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) - } else if strings.HasSuffix(taintSpec, "-") { - taintKey := taintSpec[:len(taintSpec)-1] - var effect v1.TaintEffect - if strings.Contains(taintKey, ":") { - parts := strings.Split(taintKey, ":") - taintKey = parts[0] - effect = v1.TaintEffect(parts[1]) - } - taintsToRemove = append(taintsToRemove, v1.Taint{Key: taintKey, Effect: effect}) - } else { - return nil, nil, fmt.Errorf("unknown taint spec: %v", taintSpec) - } - } - return taints, taintsToRemove, nil -} - // Complete adapts from the command line args and factory to the data required. func (o *TaintOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) (err error) { namespace, _, err := f.DefaultNamespace() @@ -243,7 +145,7 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Com return fmt.Errorf("at least one taint update is required") } - if o.taintsToAdd, o.taintsToRemove, err = parseTaints(taintArgs); err != nil { + if o.taintsToAdd, o.taintsToRemove, err = taintutils.ParseTaints(taintArgs); err != nil { return cmdutil.UsageErrorf(cmd, err.Error()) } o.builder = f.NewBuilder(true). @@ -380,21 +282,6 @@ func (o TaintOptions) RunTaint() error { }) } -// validateNoTaintOverwrites validates that when overwrite is false, to-be-updated taints don't exist in the node taint list (yet) -func validateNoTaintOverwrites(node *v1.Node, taints []v1.Taint) error { - allErrs := []error{} - oldTaints := node.Spec.Taints - for _, taint := range taints { - for _, oldTaint := range oldTaints { - if taint.Key == oldTaint.Key && taint.Effect == oldTaint.Effect { - allErrs = append(allErrs, fmt.Errorf("Node '%s' already has a taint with key (%s) and effect (%v), and --overwrite is false", node.Name, taint.Key, taint.Effect)) - break - } - } - } - return utilerrors.NewAggregate(allErrs) -} - // updateTaints applies a taint option(o) to a node in cluster after computing the net effect of operation(i.e. does it result in an overwrite?), it reports back the end result in a way that user can easily interpret. func (o TaintOptions) updateTaints(obj runtime.Object) (string, error) { node, ok := obj.(*v1.Node) @@ -402,11 +289,11 @@ func (o TaintOptions) updateTaints(obj runtime.Object) (string, error) { return "", fmt.Errorf("unexpected type %T, expected Node", obj) } if !o.overwrite { - if err := validateNoTaintOverwrites(node, o.taintsToAdd); err != nil { - return "", err + if exists := taintutils.CheckIfTaintsAlreadyExists(node.Spec.Taints, o.taintsToAdd); len(exists) != 0 { + return "", fmt.Errorf("Node %s already has %v taint(s) with same effect(s) and --overwrite is false", node.Name, exists) } } - operation, newTaints, err := reorganizeTaints(node, o.overwrite, o.taintsToAdd, o.taintsToRemove) + operation, newTaints, err := taintutils.ReorganizeTaints(node, o.overwrite, o.taintsToAdd, o.taintsToRemove) if err != nil { return "", err } diff --git a/pkg/util/taints/taints.go b/pkg/util/taints/taints.go index 869925d3a7..1822b2ec62 100644 --- a/pkg/util/taints/taints.go +++ b/pkg/util/taints/taints.go @@ -14,20 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -// package taints implements uitilites for working with taints +// package taints implements utilites for working with taints package taints import ( "fmt" + "k8s.io/apimachinery/pkg/util/sets" "strings" "k8s.io/api/core/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/helper" ) -// ParseTaint parses a taint from a string. Taint must be of the format '=:'. -func ParseTaint(st string) (v1.Taint, error) { +const ( + MODIFIED = "modified" + TAINTED = "tainted" + UNTAINTED = "untainted" +) + +// parseTaint parses a taint from a string. Taint must be off the format '=:'. +func parseTaint(st string) (v1.Taint, error) { var taint v1.Taint parts := strings.Split(st, "=") if len(parts) != 2 || len(parts[1]) == 0 || len(validation.IsQualifiedName(parts[0])) > 0 { @@ -70,7 +79,7 @@ func (t taintsVar) Set(s string) error { sts := strings.Split(s, ",") var taints []api.Taint for _, st := range sts { - taint, err := ParseTaint(st) + taint, err := parseTaint(st) if err != nil { return err } @@ -94,3 +103,199 @@ func (t taintsVar) String() string { 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. +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.Index(taintSpec, "=") != -1 && strings.Index(taintSpec, ":") != -1 { + newTaint, err := parseTaint(taintSpec) + if err != nil { + return nil, nil, err + } + // validate if taint is unique by + 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) + } else if strings.HasSuffix(taintSpec, "-") { + taintKey := taintSpec[:len(taintSpec)-1] + var effect v1.TaintEffect + if strings.Index(taintKey, ":") != -1 { + parts := strings.Split(taintKey, ":") + taintKey = parts[0] + effect = v1.TaintEffect(parts[1]) + } + taintsToRemove = append(taintsToRemove, v1.Taint{Key: taintKey, Effect: effect}) + } else { + return nil, nil, fmt.Errorf("unknown taint spec: %v", taintSpec) + } + } + 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 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) { + objCopy, err := api.Scheme.DeepCopy(node) + if err != nil { + return nil, false, err + } + newNode := objCopy.(*v1.Node) + 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) { + objCopy, err := api.Scheme.DeepCopy(node) + if err != nil { + return nil, false, err + } + newNode := objCopy.(*v1.Node) + 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 +} diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index f8b76005da..0fc3f665e2 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -75,7 +75,6 @@ import ( clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" - v1helper "k8s.io/kubernetes/pkg/api/v1/helper" nodeutil "k8s.io/kubernetes/pkg/api/v1/node" podutil "k8s.io/kubernetes/pkg/api/v1/pod" batchinternal "k8s.io/kubernetes/pkg/apis/batch" @@ -92,6 +91,7 @@ import ( sshutil "k8s.io/kubernetes/pkg/ssh" uexec "k8s.io/kubernetes/pkg/util/exec" "k8s.io/kubernetes/pkg/util/system" + taintutils "k8s.io/kubernetes/pkg/util/taints" utilversion "k8s.io/kubernetes/pkg/util/version" "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates" "k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache" @@ -2492,7 +2492,7 @@ func VerifyThatTaintIsGone(c clientset.Interface, nodeName string, taint *v1.Tai By("verifying the node doesn't have the taint " + taint.ToString()) nodeUpdated, err := c.Core().Nodes().Get(nodeName, metav1.GetOptions{}) ExpectNoError(err) - if v1helper.TaintExists(nodeUpdated.Spec.Taints, taint) { + if taintutils.TaintExists(nodeUpdated.Spec.Taints, taint) { Failf("Failed removing taint " + taint.ToString() + " of the node " + nodeName) } } @@ -2513,7 +2513,7 @@ func NodeHasTaint(c clientset.Interface, nodeName string, taint *v1.Taint) (bool nodeTaints := node.Spec.Taints - if len(nodeTaints) == 0 || !v1helper.TaintExists(nodeTaints, taint) { + if len(nodeTaints) == 0 || !taintutils.TaintExists(nodeTaints, taint) { return false, nil } return true, nil