mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 10:18:13 +00:00 
			
		
		
		
	Merge pull request #43171 from ravisantoshgudimetla/kubectl_taints_ux
Automatic merge from submit-queue Refactoring reorganize taints function in kubectl to expose operations **What this PR does / why we need it**: This adds some UX functionality when specifying taints using kubectl. For example: ``` ./kubectl.sh taint nodes XYZ dedicated1=abca2:NoSchedule node "XYZ" tainted ./kubectl.sh taint nodes XYZ dedicated1=abca1:NoSchedule --overwrite=True node "XYZ overwritten ./kubectl.sh taint nodes XYZ dedicated1- node "XYZ" untainted ./kubectl.sh taint nodes XYZ dedicated=abca1:NoSchedule dedicated1- node "XYZ" modified ``` **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #43167 **Release note**: ``` Fixed the output of kubectl taint node command with minor improvements. ```
This commit is contained in:
		| @@ -41,6 +41,12 @@ import ( | ||||
| 	utiltaints "k8s.io/kubernetes/pkg/util/taints" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	MODIFIED  = "modified" | ||||
| 	TAINTED   = "tainted" | ||||
| 	UNTAINTED = "untainted" | ||||
| ) | ||||
|  | ||||
| // TaintOptions have the data required to perform the taint operation | ||||
| type TaintOptions struct { | ||||
| 	resources      []string | ||||
| @@ -114,42 +120,54 @@ func NewCmdTaint(f cmdutil.Factory, out io.Writer) *cobra.Command { | ||||
|  | ||||
| // 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(obj runtime.Object, overwrite bool, taintsToAdd []v1.Taint, taintsToRemove []v1.Taint) ([]v1.Taint, error) { | ||||
| 	node, ok := obj.(*v1.Node) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("unexpected type %T, expected Node", obj) | ||||
| 	} | ||||
|  | ||||
| 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 { | ||||
| 		for _, taint := range *newTaints { | ||||
| 			if taint.MatchTaint(&oldTaint) { | ||||
| 				existsInNew = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if !existsInNew { | ||||
| 			newTaints = append(newTaints, oldTaint) | ||||
| 			*newTaints = append(*newTaints, oldTaint) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	allErrs := []error{} | ||||
| 	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 newTaints, utilerrors.NewAggregate(allErrs) | ||||
| 	return len(oldTaints) != len(*newTaints) | ||||
| } | ||||
|  | ||||
| func parseTaints(spec []string) ([]v1.Taint, []v1.Taint, error) { | ||||
| @@ -304,8 +322,8 @@ func (o TaintOptions) RunTaint() error { | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if err := o.updateTaints(obj); err != nil { | ||||
| 		operation, err := o.updateTaints(obj) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		newData, err := json.Marshal(obj) | ||||
| @@ -341,18 +359,13 @@ func (o TaintOptions) RunTaint() error { | ||||
| 			return o.f.PrintObject(o.cmd, mapper, outputObj, o.out) | ||||
| 		} | ||||
|  | ||||
| 		cmdutil.PrintSuccess(mapper, false, o.out, info.Mapping.Resource, info.Name, false, "tainted") | ||||
| 		cmdutil.PrintSuccess(mapper, false, o.out, info.Mapping.Resource, info.Name, false, operation) | ||||
| 		return nil | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // validateNoTaintOverwrites validates that when overwrite is false, to-be-updated taints don't exist in the node taint list (yet) | ||||
| func validateNoTaintOverwrites(obj runtime.Object, taints []v1.Taint) error { | ||||
| 	node, ok := obj.(*v1.Node) | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("unexpected type %T, expected Node", obj) | ||||
| 	} | ||||
|  | ||||
| func validateNoTaintOverwrites(node *v1.Node, taints []v1.Taint) error { | ||||
| 	allErrs := []error{} | ||||
| 	oldTaints := node.Spec.Taints | ||||
| 	for _, taint := range taints { | ||||
| @@ -366,24 +379,21 @@ func validateNoTaintOverwrites(obj runtime.Object, taints []v1.Taint) error { | ||||
| 	return utilerrors.NewAggregate(allErrs) | ||||
| } | ||||
|  | ||||
| // updateTaints updates taints of obj | ||||
| func (o TaintOptions) updateTaints(obj runtime.Object) error { | ||||
| 	if !o.overwrite { | ||||
| 		if err := validateNoTaintOverwrites(obj, o.taintsToAdd); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	newTaints, err := reorganizeTaints(obj, o.overwrite, o.taintsToAdd, o.taintsToRemove) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| // 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) | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("unexpected type %T, expected Node", obj) | ||||
| 		return "", fmt.Errorf("unexpected type %T, expected Node", obj) | ||||
| 	} | ||||
| 	if !o.overwrite { | ||||
| 		if err := validateNoTaintOverwrites(node, o.taintsToAdd); err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 	} | ||||
| 	operation, newTaints, err := reorganizeTaints(node, o.overwrite, o.taintsToAdd, o.taintsToRemove) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	node.Spec.Taints = newTaints | ||||
|  | ||||
| 	return nil | ||||
| 	return operation, nil | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Submit Queue
					Kubernetes Submit Queue