helpers.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. /*
  2. Copyright 2014 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 helper
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "strings"
  18. v1 "k8s.io/api/core/v1"
  19. "k8s.io/apimachinery/pkg/api/resource"
  20. "k8s.io/apimachinery/pkg/fields"
  21. "k8s.io/apimachinery/pkg/labels"
  22. "k8s.io/apimachinery/pkg/selection"
  23. "k8s.io/apimachinery/pkg/util/validation"
  24. "k8s.io/kubernetes/pkg/apis/core/helper"
  25. )
  26. // IsExtendedResourceName returns true if:
  27. // 1. the resource name is not in the default namespace;
  28. // 2. resource name does not have "requests." prefix,
  29. // to avoid confusion with the convention in quota
  30. // 3. it satisfies the rules in IsQualifiedName() after converted into quota resource name
  31. func IsExtendedResourceName(name v1.ResourceName) bool {
  32. if IsNativeResource(name) || strings.HasPrefix(string(name), v1.DefaultResourceRequestsPrefix) {
  33. return false
  34. }
  35. // Ensure it satisfies the rules in IsQualifiedName() after converted into quota resource name
  36. nameForQuota := fmt.Sprintf("%s%s", v1.DefaultResourceRequestsPrefix, string(name))
  37. if errs := validation.IsQualifiedName(string(nameForQuota)); len(errs) != 0 {
  38. return false
  39. }
  40. return true
  41. }
  42. // IsPrefixedNativeResource returns true if the resource name is in the
  43. // *kubernetes.io/ namespace.
  44. func IsPrefixedNativeResource(name v1.ResourceName) bool {
  45. return strings.Contains(string(name), v1.ResourceDefaultNamespacePrefix)
  46. }
  47. // IsNativeResource returns true if the resource name is in the
  48. // *kubernetes.io/ namespace. Partially-qualified (unprefixed) names are
  49. // implicitly in the kubernetes.io/ namespace.
  50. func IsNativeResource(name v1.ResourceName) bool {
  51. return !strings.Contains(string(name), "/") ||
  52. IsPrefixedNativeResource(name)
  53. }
  54. // IsHugePageResourceName returns true if the resource name has the huge page
  55. // resource prefix.
  56. func IsHugePageResourceName(name v1.ResourceName) bool {
  57. return strings.HasPrefix(string(name), v1.ResourceHugePagesPrefix)
  58. }
  59. // HugePageResourceName returns a ResourceName with the canonical hugepage
  60. // prefix prepended for the specified page size. The page size is converted
  61. // to its canonical representation.
  62. func HugePageResourceName(pageSize resource.Quantity) v1.ResourceName {
  63. return v1.ResourceName(fmt.Sprintf("%s%s", v1.ResourceHugePagesPrefix, pageSize.String()))
  64. }
  65. // HugePageSizeFromResourceName returns the page size for the specified huge page
  66. // resource name. If the specified input is not a valid huge page resource name
  67. // an error is returned.
  68. func HugePageSizeFromResourceName(name v1.ResourceName) (resource.Quantity, error) {
  69. if !IsHugePageResourceName(name) {
  70. return resource.Quantity{}, fmt.Errorf("resource name: %s is an invalid hugepage name", name)
  71. }
  72. pageSize := strings.TrimPrefix(string(name), v1.ResourceHugePagesPrefix)
  73. return resource.ParseQuantity(pageSize)
  74. }
  75. // HugePageUnitSizeFromByteSize returns hugepage size has the format.
  76. // `size` must be guaranteed to divisible into the largest units that can be expressed.
  77. // <size><unit-prefix>B (1024 = "1KB", 1048576 = "1MB", etc).
  78. func HugePageUnitSizeFromByteSize(size int64) (string, error) {
  79. // hugePageSizeUnitList is borrowed from opencontainers/runc/libcontainer/cgroups/utils.go
  80. var hugePageSizeUnitList = []string{"B", "KB", "MB", "GB", "TB", "PB"}
  81. idx := 0
  82. len := len(hugePageSizeUnitList) - 1
  83. for size%1024 == 0 && idx < len {
  84. size /= 1024
  85. idx++
  86. }
  87. if size > 1024 && idx < len {
  88. return "", fmt.Errorf("size: %d%s must be guaranteed to divisible into the largest units", size, hugePageSizeUnitList[idx])
  89. }
  90. return fmt.Sprintf("%d%s", size, hugePageSizeUnitList[idx]), nil
  91. }
  92. // IsOvercommitAllowed returns true if the resource is in the default
  93. // namespace and is not hugepages.
  94. func IsOvercommitAllowed(name v1.ResourceName) bool {
  95. return IsNativeResource(name) &&
  96. !IsHugePageResourceName(name)
  97. }
  98. func IsAttachableVolumeResourceName(name v1.ResourceName) bool {
  99. return strings.HasPrefix(string(name), v1.ResourceAttachableVolumesPrefix)
  100. }
  101. // Extended and Hugepages resources
  102. func IsScalarResourceName(name v1.ResourceName) bool {
  103. return IsExtendedResourceName(name) || IsHugePageResourceName(name) ||
  104. IsPrefixedNativeResource(name) || IsAttachableVolumeResourceName(name)
  105. }
  106. // this function aims to check if the service's ClusterIP is set or not
  107. // the objective is not to perform validation here
  108. func IsServiceIPSet(service *v1.Service) bool {
  109. return service.Spec.ClusterIP != v1.ClusterIPNone && service.Spec.ClusterIP != ""
  110. }
  111. // TODO: make method on LoadBalancerStatus?
  112. func LoadBalancerStatusEqual(l, r *v1.LoadBalancerStatus) bool {
  113. return ingressSliceEqual(l.Ingress, r.Ingress)
  114. }
  115. func ingressSliceEqual(lhs, rhs []v1.LoadBalancerIngress) bool {
  116. if len(lhs) != len(rhs) {
  117. return false
  118. }
  119. for i := range lhs {
  120. if !ingressEqual(&lhs[i], &rhs[i]) {
  121. return false
  122. }
  123. }
  124. return true
  125. }
  126. func ingressEqual(lhs, rhs *v1.LoadBalancerIngress) bool {
  127. if lhs.IP != rhs.IP {
  128. return false
  129. }
  130. if lhs.Hostname != rhs.Hostname {
  131. return false
  132. }
  133. return true
  134. }
  135. // GetAccessModesAsString returns a string representation of an array of access modes.
  136. // modes, when present, are always in the same order: RWO,ROX,RWX.
  137. func GetAccessModesAsString(modes []v1.PersistentVolumeAccessMode) string {
  138. modes = removeDuplicateAccessModes(modes)
  139. modesStr := []string{}
  140. if containsAccessMode(modes, v1.ReadWriteOnce) {
  141. modesStr = append(modesStr, "RWO")
  142. }
  143. if containsAccessMode(modes, v1.ReadOnlyMany) {
  144. modesStr = append(modesStr, "ROX")
  145. }
  146. if containsAccessMode(modes, v1.ReadWriteMany) {
  147. modesStr = append(modesStr, "RWX")
  148. }
  149. return strings.Join(modesStr, ",")
  150. }
  151. // GetAccessModesAsString returns an array of AccessModes from a string created by GetAccessModesAsString
  152. func GetAccessModesFromString(modes string) []v1.PersistentVolumeAccessMode {
  153. strmodes := strings.Split(modes, ",")
  154. accessModes := []v1.PersistentVolumeAccessMode{}
  155. for _, s := range strmodes {
  156. s = strings.Trim(s, " ")
  157. switch {
  158. case s == "RWO":
  159. accessModes = append(accessModes, v1.ReadWriteOnce)
  160. case s == "ROX":
  161. accessModes = append(accessModes, v1.ReadOnlyMany)
  162. case s == "RWX":
  163. accessModes = append(accessModes, v1.ReadWriteMany)
  164. }
  165. }
  166. return accessModes
  167. }
  168. // removeDuplicateAccessModes returns an array of access modes without any duplicates
  169. func removeDuplicateAccessModes(modes []v1.PersistentVolumeAccessMode) []v1.PersistentVolumeAccessMode {
  170. accessModes := []v1.PersistentVolumeAccessMode{}
  171. for _, m := range modes {
  172. if !containsAccessMode(accessModes, m) {
  173. accessModes = append(accessModes, m)
  174. }
  175. }
  176. return accessModes
  177. }
  178. func containsAccessMode(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool {
  179. for _, m := range modes {
  180. if m == mode {
  181. return true
  182. }
  183. }
  184. return false
  185. }
  186. // NodeSelectorRequirementsAsSelector converts the []NodeSelectorRequirement api type into a struct that implements
  187. // labels.Selector.
  188. func NodeSelectorRequirementsAsSelector(nsm []v1.NodeSelectorRequirement) (labels.Selector, error) {
  189. if len(nsm) == 0 {
  190. return labels.Nothing(), nil
  191. }
  192. selector := labels.NewSelector()
  193. for _, expr := range nsm {
  194. var op selection.Operator
  195. switch expr.Operator {
  196. case v1.NodeSelectorOpIn:
  197. op = selection.In
  198. case v1.NodeSelectorOpNotIn:
  199. op = selection.NotIn
  200. case v1.NodeSelectorOpExists:
  201. op = selection.Exists
  202. case v1.NodeSelectorOpDoesNotExist:
  203. op = selection.DoesNotExist
  204. case v1.NodeSelectorOpGt:
  205. op = selection.GreaterThan
  206. case v1.NodeSelectorOpLt:
  207. op = selection.LessThan
  208. default:
  209. return nil, fmt.Errorf("%q is not a valid node selector operator", expr.Operator)
  210. }
  211. r, err := labels.NewRequirement(expr.Key, op, expr.Values)
  212. if err != nil {
  213. return nil, err
  214. }
  215. selector = selector.Add(*r)
  216. }
  217. return selector, nil
  218. }
  219. // NodeSelectorRequirementsAsFieldSelector converts the []NodeSelectorRequirement core type into a struct that implements
  220. // fields.Selector.
  221. func NodeSelectorRequirementsAsFieldSelector(nsm []v1.NodeSelectorRequirement) (fields.Selector, error) {
  222. if len(nsm) == 0 {
  223. return fields.Nothing(), nil
  224. }
  225. selectors := []fields.Selector{}
  226. for _, expr := range nsm {
  227. switch expr.Operator {
  228. case v1.NodeSelectorOpIn:
  229. if len(expr.Values) != 1 {
  230. return nil, fmt.Errorf("unexpected number of value (%d) for node field selector operator %q",
  231. len(expr.Values), expr.Operator)
  232. }
  233. selectors = append(selectors, fields.OneTermEqualSelector(expr.Key, expr.Values[0]))
  234. case v1.NodeSelectorOpNotIn:
  235. if len(expr.Values) != 1 {
  236. return nil, fmt.Errorf("unexpected number of value (%d) for node field selector operator %q",
  237. len(expr.Values), expr.Operator)
  238. }
  239. selectors = append(selectors, fields.OneTermNotEqualSelector(expr.Key, expr.Values[0]))
  240. default:
  241. return nil, fmt.Errorf("%q is not a valid node field selector operator", expr.Operator)
  242. }
  243. }
  244. return fields.AndSelectors(selectors...), nil
  245. }
  246. // NodeSelectorRequirementKeysExistInNodeSelectorTerms checks if a NodeSelectorTerm with key is already specified in terms
  247. func NodeSelectorRequirementKeysExistInNodeSelectorTerms(reqs []v1.NodeSelectorRequirement, terms []v1.NodeSelectorTerm) bool {
  248. for _, req := range reqs {
  249. for _, term := range terms {
  250. for _, r := range term.MatchExpressions {
  251. if r.Key == req.Key {
  252. return true
  253. }
  254. }
  255. }
  256. }
  257. return false
  258. }
  259. // MatchNodeSelectorTerms checks whether the node labels and fields match node selector terms in ORed;
  260. // nil or empty term matches no objects.
  261. func MatchNodeSelectorTerms(
  262. nodeSelectorTerms []v1.NodeSelectorTerm,
  263. nodeLabels labels.Set,
  264. nodeFields fields.Set,
  265. ) bool {
  266. for _, req := range nodeSelectorTerms {
  267. // nil or empty term selects no objects
  268. if len(req.MatchExpressions) == 0 && len(req.MatchFields) == 0 {
  269. continue
  270. }
  271. if len(req.MatchExpressions) != 0 {
  272. labelSelector, err := NodeSelectorRequirementsAsSelector(req.MatchExpressions)
  273. if err != nil || !labelSelector.Matches(nodeLabels) {
  274. continue
  275. }
  276. }
  277. if len(req.MatchFields) != 0 {
  278. fieldSelector, err := NodeSelectorRequirementsAsFieldSelector(req.MatchFields)
  279. if err != nil || !fieldSelector.Matches(nodeFields) {
  280. continue
  281. }
  282. }
  283. return true
  284. }
  285. return false
  286. }
  287. // TopologySelectorRequirementsAsSelector converts the []TopologySelectorLabelRequirement api type into a struct
  288. // that implements labels.Selector.
  289. func TopologySelectorRequirementsAsSelector(tsm []v1.TopologySelectorLabelRequirement) (labels.Selector, error) {
  290. if len(tsm) == 0 {
  291. return labels.Nothing(), nil
  292. }
  293. selector := labels.NewSelector()
  294. for _, expr := range tsm {
  295. r, err := labels.NewRequirement(expr.Key, selection.In, expr.Values)
  296. if err != nil {
  297. return nil, err
  298. }
  299. selector = selector.Add(*r)
  300. }
  301. return selector, nil
  302. }
  303. // MatchTopologySelectorTerms checks whether given labels match topology selector terms in ORed;
  304. // nil or empty term matches no objects; while empty term list matches all objects.
  305. func MatchTopologySelectorTerms(topologySelectorTerms []v1.TopologySelectorTerm, lbls labels.Set) bool {
  306. if len(topologySelectorTerms) == 0 {
  307. // empty term list matches all objects
  308. return true
  309. }
  310. for _, req := range topologySelectorTerms {
  311. // nil or empty term selects no objects
  312. if len(req.MatchLabelExpressions) == 0 {
  313. continue
  314. }
  315. labelSelector, err := TopologySelectorRequirementsAsSelector(req.MatchLabelExpressions)
  316. if err != nil || !labelSelector.Matches(lbls) {
  317. continue
  318. }
  319. return true
  320. }
  321. return false
  322. }
  323. // AddOrUpdateTolerationInPodSpec tries to add a toleration to the toleration list in PodSpec.
  324. // Returns true if something was updated, false otherwise.
  325. func AddOrUpdateTolerationInPodSpec(spec *v1.PodSpec, toleration *v1.Toleration) bool {
  326. podTolerations := spec.Tolerations
  327. var newTolerations []v1.Toleration
  328. updated := false
  329. for i := range podTolerations {
  330. if toleration.MatchToleration(&podTolerations[i]) {
  331. if helper.Semantic.DeepEqual(toleration, podTolerations[i]) {
  332. return false
  333. }
  334. newTolerations = append(newTolerations, *toleration)
  335. updated = true
  336. continue
  337. }
  338. newTolerations = append(newTolerations, podTolerations[i])
  339. }
  340. if !updated {
  341. newTolerations = append(newTolerations, *toleration)
  342. }
  343. spec.Tolerations = newTolerations
  344. return true
  345. }
  346. // AddOrUpdateTolerationInPod tries to add a toleration to the pod's toleration list.
  347. // Returns true if something was updated, false otherwise.
  348. func AddOrUpdateTolerationInPod(pod *v1.Pod, toleration *v1.Toleration) bool {
  349. return AddOrUpdateTolerationInPodSpec(&pod.Spec, toleration)
  350. }
  351. // TolerationsTolerateTaint checks if taint is tolerated by any of the tolerations.
  352. func TolerationsTolerateTaint(tolerations []v1.Toleration, taint *v1.Taint) bool {
  353. for i := range tolerations {
  354. if tolerations[i].ToleratesTaint(taint) {
  355. return true
  356. }
  357. }
  358. return false
  359. }
  360. type taintsFilterFunc func(*v1.Taint) bool
  361. // TolerationsTolerateTaintsWithFilter checks if given tolerations tolerates
  362. // all the taints that apply to the filter in given taint list.
  363. // DEPRECATED: Please use FindMatchingUntoleratedTaint instead.
  364. func TolerationsTolerateTaintsWithFilter(tolerations []v1.Toleration, taints []v1.Taint, applyFilter taintsFilterFunc) bool {
  365. _, isUntolerated := FindMatchingUntoleratedTaint(taints, tolerations, applyFilter)
  366. return !isUntolerated
  367. }
  368. // FindMatchingUntoleratedTaint checks if the given tolerations tolerates
  369. // all the filtered taints, and returns the first taint without a toleration
  370. func FindMatchingUntoleratedTaint(taints []v1.Taint, tolerations []v1.Toleration, inclusionFilter taintsFilterFunc) (v1.Taint, bool) {
  371. filteredTaints := getFilteredTaints(taints, inclusionFilter)
  372. for _, taint := range filteredTaints {
  373. if !TolerationsTolerateTaint(tolerations, &taint) {
  374. return taint, true
  375. }
  376. }
  377. return v1.Taint{}, false
  378. }
  379. // getFilteredTaints returns a list of taints satisfying the filter predicate
  380. func getFilteredTaints(taints []v1.Taint, inclusionFilter taintsFilterFunc) []v1.Taint {
  381. if inclusionFilter == nil {
  382. return taints
  383. }
  384. filteredTaints := []v1.Taint{}
  385. for _, taint := range taints {
  386. if !inclusionFilter(&taint) {
  387. continue
  388. }
  389. filteredTaints = append(filteredTaints, taint)
  390. }
  391. return filteredTaints
  392. }
  393. // Returns true and list of Tolerations matching all Taints if all are tolerated, or false otherwise.
  394. func GetMatchingTolerations(taints []v1.Taint, tolerations []v1.Toleration) (bool, []v1.Toleration) {
  395. if len(taints) == 0 {
  396. return true, []v1.Toleration{}
  397. }
  398. if len(tolerations) == 0 && len(taints) > 0 {
  399. return false, []v1.Toleration{}
  400. }
  401. result := []v1.Toleration{}
  402. for i := range taints {
  403. tolerated := false
  404. for j := range tolerations {
  405. if tolerations[j].ToleratesTaint(&taints[i]) {
  406. result = append(result, tolerations[j])
  407. tolerated = true
  408. break
  409. }
  410. }
  411. if !tolerated {
  412. return false, []v1.Toleration{}
  413. }
  414. }
  415. return true, result
  416. }
  417. func GetAvoidPodsFromNodeAnnotations(annotations map[string]string) (v1.AvoidPods, error) {
  418. var avoidPods v1.AvoidPods
  419. if len(annotations) > 0 && annotations[v1.PreferAvoidPodsAnnotationKey] != "" {
  420. err := json.Unmarshal([]byte(annotations[v1.PreferAvoidPodsAnnotationKey]), &avoidPods)
  421. if err != nil {
  422. return avoidPods, err
  423. }
  424. }
  425. return avoidPods, nil
  426. }
  427. // GetPersistentVolumeClass returns StorageClassName.
  428. func GetPersistentVolumeClass(volume *v1.PersistentVolume) string {
  429. // Use beta annotation first
  430. if class, found := volume.Annotations[v1.BetaStorageClassAnnotation]; found {
  431. return class
  432. }
  433. return volume.Spec.StorageClassName
  434. }
  435. // GetPersistentVolumeClaimClass returns StorageClassName. If no storage class was
  436. // requested, it returns "".
  437. func GetPersistentVolumeClaimClass(claim *v1.PersistentVolumeClaim) string {
  438. // Use beta annotation first
  439. if class, found := claim.Annotations[v1.BetaStorageClassAnnotation]; found {
  440. return class
  441. }
  442. if claim.Spec.StorageClassName != nil {
  443. return *claim.Spec.StorageClassName
  444. }
  445. return ""
  446. }
  447. // ScopedResourceSelectorRequirementsAsSelector converts the ScopedResourceSelectorRequirement api type into a struct that implements
  448. // labels.Selector.
  449. func ScopedResourceSelectorRequirementsAsSelector(ssr v1.ScopedResourceSelectorRequirement) (labels.Selector, error) {
  450. selector := labels.NewSelector()
  451. var op selection.Operator
  452. switch ssr.Operator {
  453. case v1.ScopeSelectorOpIn:
  454. op = selection.In
  455. case v1.ScopeSelectorOpNotIn:
  456. op = selection.NotIn
  457. case v1.ScopeSelectorOpExists:
  458. op = selection.Exists
  459. case v1.ScopeSelectorOpDoesNotExist:
  460. op = selection.DoesNotExist
  461. default:
  462. return nil, fmt.Errorf("%q is not a valid scope selector operator", ssr.Operator)
  463. }
  464. r, err := labels.NewRequirement(string(ssr.ScopeName), op, ssr.Values)
  465. if err != nil {
  466. return nil, err
  467. }
  468. selector = selector.Add(*r)
  469. return selector, nil
  470. }