taints.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // package taints implements utilities for working with taints
  14. package taints
  15. import (
  16. "fmt"
  17. "strings"
  18. "k8s.io/api/core/v1"
  19. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  20. "k8s.io/apimachinery/pkg/util/sets"
  21. "k8s.io/apimachinery/pkg/util/validation"
  22. api "k8s.io/kubernetes/pkg/apis/core"
  23. "k8s.io/kubernetes/pkg/apis/core/helper"
  24. )
  25. const (
  26. MODIFIED = "modified"
  27. TAINTED = "tainted"
  28. UNTAINTED = "untainted"
  29. )
  30. // parseTaint parses a taint from a string, whose form must be either
  31. // '<key>=<value>:<effect>', '<key>:<effect>', or '<key>'.
  32. func parseTaint(st string) (v1.Taint, error) {
  33. var taint v1.Taint
  34. var key string
  35. var value string
  36. var effect v1.TaintEffect
  37. parts := strings.Split(st, ":")
  38. switch len(parts) {
  39. case 1:
  40. key = parts[0]
  41. case 2:
  42. effect = v1.TaintEffect(parts[1])
  43. if err := validateTaintEffect(effect); err != nil {
  44. return taint, err
  45. }
  46. partsKV := strings.Split(parts[0], "=")
  47. if len(partsKV) > 2 {
  48. return taint, fmt.Errorf("invalid taint spec: %v", st)
  49. }
  50. key = partsKV[0]
  51. if len(partsKV) == 2 {
  52. value = partsKV[1]
  53. if errs := validation.IsValidLabelValue(value); len(errs) > 0 {
  54. return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
  55. }
  56. }
  57. default:
  58. return taint, fmt.Errorf("invalid taint spec: %v", st)
  59. }
  60. if errs := validation.IsQualifiedName(key); len(errs) > 0 {
  61. return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
  62. }
  63. taint.Key = key
  64. taint.Value = value
  65. taint.Effect = effect
  66. return taint, nil
  67. }
  68. func validateTaintEffect(effect v1.TaintEffect) error {
  69. if effect != v1.TaintEffectNoSchedule && effect != v1.TaintEffectPreferNoSchedule && effect != v1.TaintEffectNoExecute {
  70. return fmt.Errorf("invalid taint effect: %v, unsupported taint effect", effect)
  71. }
  72. return nil
  73. }
  74. // NewTaintsVar wraps []api.Taint in a struct that implements flag.Value to allow taints to be
  75. // bound to command line flags.
  76. func NewTaintsVar(ptr *[]api.Taint) taintsVar {
  77. return taintsVar{
  78. ptr: ptr,
  79. }
  80. }
  81. type taintsVar struct {
  82. ptr *[]api.Taint
  83. }
  84. func (t taintsVar) Set(s string) error {
  85. if len(s) == 0 {
  86. *t.ptr = nil
  87. return nil
  88. }
  89. sts := strings.Split(s, ",")
  90. var taints []api.Taint
  91. for _, st := range sts {
  92. taint, err := parseTaint(st)
  93. if err != nil {
  94. return err
  95. }
  96. taints = append(taints, api.Taint{Key: taint.Key, Value: taint.Value, Effect: api.TaintEffect(taint.Effect)})
  97. }
  98. *t.ptr = taints
  99. return nil
  100. }
  101. func (t taintsVar) String() string {
  102. if len(*t.ptr) == 0 {
  103. return ""
  104. }
  105. var taints []string
  106. for _, taint := range *t.ptr {
  107. taints = append(taints, fmt.Sprintf("%s=%s:%s", taint.Key, taint.Value, taint.Effect))
  108. }
  109. return strings.Join(taints, ",")
  110. }
  111. func (t taintsVar) Type() string {
  112. return "[]api.Taint"
  113. }
  114. // ParseTaints takes a spec which is an array and creates slices for new taints to be added, taints to be deleted.
  115. // It also validates the spec. For example, the form `<key>` may be used to remove a taint, but not to add one.
  116. func ParseTaints(spec []string) ([]v1.Taint, []v1.Taint, error) {
  117. var taints, taintsToRemove []v1.Taint
  118. uniqueTaints := map[v1.TaintEffect]sets.String{}
  119. for _, taintSpec := range spec {
  120. if strings.HasSuffix(taintSpec, "-") {
  121. taintToRemove, err := parseTaint(strings.TrimSuffix(taintSpec, "-"))
  122. if err != nil {
  123. return nil, nil, err
  124. }
  125. taintsToRemove = append(taintsToRemove, v1.Taint{Key: taintToRemove.Key, Effect: taintToRemove.Effect})
  126. } else {
  127. newTaint, err := parseTaint(taintSpec)
  128. if err != nil {
  129. return nil, nil, err
  130. }
  131. // validate that the taint has an effect, which is required to add the taint
  132. if len(newTaint.Effect) == 0 {
  133. return nil, nil, fmt.Errorf("invalid taint spec: %v", taintSpec)
  134. }
  135. // validate if taint is unique by <key, effect>
  136. if len(uniqueTaints[newTaint.Effect]) > 0 && uniqueTaints[newTaint.Effect].Has(newTaint.Key) {
  137. return nil, nil, fmt.Errorf("duplicated taints with the same key and effect: %v", newTaint)
  138. }
  139. // add taint to existingTaints for uniqueness check
  140. if len(uniqueTaints[newTaint.Effect]) == 0 {
  141. uniqueTaints[newTaint.Effect] = sets.String{}
  142. }
  143. uniqueTaints[newTaint.Effect].Insert(newTaint.Key)
  144. taints = append(taints, newTaint)
  145. }
  146. }
  147. return taints, taintsToRemove, nil
  148. }
  149. // ReorganizeTaints returns the updated set of taints, taking into account old taints that were not updated,
  150. // old taints that were updated, old taints that were deleted, and new taints.
  151. func ReorganizeTaints(node *v1.Node, overwrite bool, taintsToAdd []v1.Taint, taintsToRemove []v1.Taint) (string, []v1.Taint, error) {
  152. newTaints := append([]v1.Taint{}, taintsToAdd...)
  153. oldTaints := node.Spec.Taints
  154. // add taints that already existing but not updated to newTaints
  155. added := addTaints(oldTaints, &newTaints)
  156. allErrs, deleted := deleteTaints(taintsToRemove, &newTaints)
  157. if (added && deleted) || overwrite {
  158. return MODIFIED, newTaints, utilerrors.NewAggregate(allErrs)
  159. } else if added {
  160. return TAINTED, newTaints, utilerrors.NewAggregate(allErrs)
  161. }
  162. return UNTAINTED, newTaints, utilerrors.NewAggregate(allErrs)
  163. }
  164. // deleteTaints deletes the given taints from the node's taintlist.
  165. func deleteTaints(taintsToRemove []v1.Taint, newTaints *[]v1.Taint) ([]error, bool) {
  166. allErrs := []error{}
  167. var removed bool
  168. for _, taintToRemove := range taintsToRemove {
  169. removed = false
  170. if len(taintToRemove.Effect) > 0 {
  171. *newTaints, removed = DeleteTaint(*newTaints, &taintToRemove)
  172. } else {
  173. *newTaints, removed = DeleteTaintsByKey(*newTaints, taintToRemove.Key)
  174. }
  175. if !removed {
  176. allErrs = append(allErrs, fmt.Errorf("taint %q not found", taintToRemove.ToString()))
  177. }
  178. }
  179. return allErrs, removed
  180. }
  181. // addTaints adds the newTaints list to existing ones and updates the newTaints List.
  182. // TODO: This needs a rewrite to take only the new values instead of appended newTaints list to be consistent.
  183. func addTaints(oldTaints []v1.Taint, newTaints *[]v1.Taint) bool {
  184. for _, oldTaint := range oldTaints {
  185. existsInNew := false
  186. for _, taint := range *newTaints {
  187. if taint.MatchTaint(&oldTaint) {
  188. existsInNew = true
  189. break
  190. }
  191. }
  192. if !existsInNew {
  193. *newTaints = append(*newTaints, oldTaint)
  194. }
  195. }
  196. return len(oldTaints) != len(*newTaints)
  197. }
  198. // CheckIfTaintsAlreadyExists checks if the node already has taints that we want to add and returns a string with taint keys.
  199. func CheckIfTaintsAlreadyExists(oldTaints []v1.Taint, taints []v1.Taint) string {
  200. var existingTaintList = make([]string, 0)
  201. for _, taint := range taints {
  202. for _, oldTaint := range oldTaints {
  203. if taint.Key == oldTaint.Key && taint.Effect == oldTaint.Effect {
  204. existingTaintList = append(existingTaintList, taint.Key)
  205. }
  206. }
  207. }
  208. return strings.Join(existingTaintList, ",")
  209. }
  210. // DeleteTaintsByKey removes all the taints that have the same key to given taintKey
  211. func DeleteTaintsByKey(taints []v1.Taint, taintKey string) ([]v1.Taint, bool) {
  212. newTaints := []v1.Taint{}
  213. deleted := false
  214. for i := range taints {
  215. if taintKey == taints[i].Key {
  216. deleted = true
  217. continue
  218. }
  219. newTaints = append(newTaints, taints[i])
  220. }
  221. return newTaints, deleted
  222. }
  223. // DeleteTaint removes all the taints that have the same key and effect to given taintToDelete.
  224. func DeleteTaint(taints []v1.Taint, taintToDelete *v1.Taint) ([]v1.Taint, bool) {
  225. newTaints := []v1.Taint{}
  226. deleted := false
  227. for i := range taints {
  228. if taintToDelete.MatchTaint(&taints[i]) {
  229. deleted = true
  230. continue
  231. }
  232. newTaints = append(newTaints, taints[i])
  233. }
  234. return newTaints, deleted
  235. }
  236. // RemoveTaint tries to remove a taint from annotations list. Returns a new copy of updated Node and true if something was updated
  237. // false otherwise.
  238. func RemoveTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
  239. newNode := node.DeepCopy()
  240. nodeTaints := newNode.Spec.Taints
  241. if len(nodeTaints) == 0 {
  242. return newNode, false, nil
  243. }
  244. if !TaintExists(nodeTaints, taint) {
  245. return newNode, false, nil
  246. }
  247. newTaints, _ := DeleteTaint(nodeTaints, taint)
  248. newNode.Spec.Taints = newTaints
  249. return newNode, true, nil
  250. }
  251. // AddOrUpdateTaint tries to add a taint to annotations list. Returns a new copy of updated Node and true if something was updated
  252. // false otherwise.
  253. func AddOrUpdateTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
  254. newNode := node.DeepCopy()
  255. nodeTaints := newNode.Spec.Taints
  256. var newTaints []v1.Taint
  257. updated := false
  258. for i := range nodeTaints {
  259. if taint.MatchTaint(&nodeTaints[i]) {
  260. if helper.Semantic.DeepEqual(*taint, nodeTaints[i]) {
  261. return newNode, false, nil
  262. }
  263. newTaints = append(newTaints, *taint)
  264. updated = true
  265. continue
  266. }
  267. newTaints = append(newTaints, nodeTaints[i])
  268. }
  269. if !updated {
  270. newTaints = append(newTaints, *taint)
  271. }
  272. newNode.Spec.Taints = newTaints
  273. return newNode, true, nil
  274. }
  275. // TaintExists checks if the given taint exists in list of taints. Returns true if exists false otherwise.
  276. func TaintExists(taints []v1.Taint, taintToFind *v1.Taint) bool {
  277. for _, taint := range taints {
  278. if taint.MatchTaint(taintToFind) {
  279. return true
  280. }
  281. }
  282. return false
  283. }
  284. func TaintSetDiff(t1, t2 []v1.Taint) (taintsToAdd []*v1.Taint, taintsToRemove []*v1.Taint) {
  285. for _, taint := range t1 {
  286. if !TaintExists(t2, &taint) {
  287. t := taint
  288. taintsToAdd = append(taintsToAdd, &t)
  289. }
  290. }
  291. for _, taint := range t2 {
  292. if !TaintExists(t1, &taint) {
  293. t := taint
  294. taintsToRemove = append(taintsToRemove, &t)
  295. }
  296. }
  297. return
  298. }
  299. func TaintSetFilter(taints []v1.Taint, fn func(*v1.Taint) bool) []v1.Taint {
  300. res := []v1.Taint{}
  301. for _, taint := range taints {
  302. if fn(&taint) {
  303. res = append(res, taint)
  304. }
  305. }
  306. return res
  307. }