helpers.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  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. "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. // IsOvercommitAllowed returns true if the resource is in the default
  76. // namespace and is not hugepages.
  77. func IsOvercommitAllowed(name v1.ResourceName) bool {
  78. return IsNativeResource(name) &&
  79. !IsHugePageResourceName(name)
  80. }
  81. func IsAttachableVolumeResourceName(name v1.ResourceName) bool {
  82. return strings.HasPrefix(string(name), v1.ResourceAttachableVolumesPrefix)
  83. }
  84. // Extended and Hugepages resources
  85. func IsScalarResourceName(name v1.ResourceName) bool {
  86. return IsExtendedResourceName(name) || IsHugePageResourceName(name) ||
  87. IsPrefixedNativeResource(name) || IsAttachableVolumeResourceName(name)
  88. }
  89. // this function aims to check if the service's ClusterIP is set or not
  90. // the objective is not to perform validation here
  91. func IsServiceIPSet(service *v1.Service) bool {
  92. return service.Spec.ClusterIP != v1.ClusterIPNone && service.Spec.ClusterIP != ""
  93. }
  94. // TODO: make method on LoadBalancerStatus?
  95. func LoadBalancerStatusEqual(l, r *v1.LoadBalancerStatus) bool {
  96. return ingressSliceEqual(l.Ingress, r.Ingress)
  97. }
  98. func ingressSliceEqual(lhs, rhs []v1.LoadBalancerIngress) bool {
  99. if len(lhs) != len(rhs) {
  100. return false
  101. }
  102. for i := range lhs {
  103. if !ingressEqual(&lhs[i], &rhs[i]) {
  104. return false
  105. }
  106. }
  107. return true
  108. }
  109. func ingressEqual(lhs, rhs *v1.LoadBalancerIngress) bool {
  110. if lhs.IP != rhs.IP {
  111. return false
  112. }
  113. if lhs.Hostname != rhs.Hostname {
  114. return false
  115. }
  116. return true
  117. }
  118. // TODO: make method on LoadBalancerStatus?
  119. func LoadBalancerStatusDeepCopy(lb *v1.LoadBalancerStatus) *v1.LoadBalancerStatus {
  120. c := &v1.LoadBalancerStatus{}
  121. c.Ingress = make([]v1.LoadBalancerIngress, len(lb.Ingress))
  122. for i := range lb.Ingress {
  123. c.Ingress[i] = lb.Ingress[i]
  124. }
  125. return c
  126. }
  127. // GetAccessModesAsString returns a string representation of an array of access modes.
  128. // modes, when present, are always in the same order: RWO,ROX,RWX.
  129. func GetAccessModesAsString(modes []v1.PersistentVolumeAccessMode) string {
  130. modes = removeDuplicateAccessModes(modes)
  131. modesStr := []string{}
  132. if containsAccessMode(modes, v1.ReadWriteOnce) {
  133. modesStr = append(modesStr, "RWO")
  134. }
  135. if containsAccessMode(modes, v1.ReadOnlyMany) {
  136. modesStr = append(modesStr, "ROX")
  137. }
  138. if containsAccessMode(modes, v1.ReadWriteMany) {
  139. modesStr = append(modesStr, "RWX")
  140. }
  141. return strings.Join(modesStr, ",")
  142. }
  143. // GetAccessModesAsString returns an array of AccessModes from a string created by GetAccessModesAsString
  144. func GetAccessModesFromString(modes string) []v1.PersistentVolumeAccessMode {
  145. strmodes := strings.Split(modes, ",")
  146. accessModes := []v1.PersistentVolumeAccessMode{}
  147. for _, s := range strmodes {
  148. s = strings.Trim(s, " ")
  149. switch {
  150. case s == "RWO":
  151. accessModes = append(accessModes, v1.ReadWriteOnce)
  152. case s == "ROX":
  153. accessModes = append(accessModes, v1.ReadOnlyMany)
  154. case s == "RWX":
  155. accessModes = append(accessModes, v1.ReadWriteMany)
  156. }
  157. }
  158. return accessModes
  159. }
  160. // removeDuplicateAccessModes returns an array of access modes without any duplicates
  161. func removeDuplicateAccessModes(modes []v1.PersistentVolumeAccessMode) []v1.PersistentVolumeAccessMode {
  162. accessModes := []v1.PersistentVolumeAccessMode{}
  163. for _, m := range modes {
  164. if !containsAccessMode(accessModes, m) {
  165. accessModes = append(accessModes, m)
  166. }
  167. }
  168. return accessModes
  169. }
  170. func containsAccessMode(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool {
  171. for _, m := range modes {
  172. if m == mode {
  173. return true
  174. }
  175. }
  176. return false
  177. }
  178. // NodeSelectorRequirementsAsSelector converts the []NodeSelectorRequirement api type into a struct that implements
  179. // labels.Selector.
  180. func NodeSelectorRequirementsAsSelector(nsm []v1.NodeSelectorRequirement) (labels.Selector, error) {
  181. if len(nsm) == 0 {
  182. return labels.Nothing(), nil
  183. }
  184. selector := labels.NewSelector()
  185. for _, expr := range nsm {
  186. var op selection.Operator
  187. switch expr.Operator {
  188. case v1.NodeSelectorOpIn:
  189. op = selection.In
  190. case v1.NodeSelectorOpNotIn:
  191. op = selection.NotIn
  192. case v1.NodeSelectorOpExists:
  193. op = selection.Exists
  194. case v1.NodeSelectorOpDoesNotExist:
  195. op = selection.DoesNotExist
  196. case v1.NodeSelectorOpGt:
  197. op = selection.GreaterThan
  198. case v1.NodeSelectorOpLt:
  199. op = selection.LessThan
  200. default:
  201. return nil, fmt.Errorf("%q is not a valid node selector operator", expr.Operator)
  202. }
  203. r, err := labels.NewRequirement(expr.Key, op, expr.Values)
  204. if err != nil {
  205. return nil, err
  206. }
  207. selector = selector.Add(*r)
  208. }
  209. return selector, nil
  210. }
  211. // NodeSelectorRequirementsAsFieldSelector converts the []NodeSelectorRequirement core type into a struct that implements
  212. // fields.Selector.
  213. func NodeSelectorRequirementsAsFieldSelector(nsm []v1.NodeSelectorRequirement) (fields.Selector, error) {
  214. if len(nsm) == 0 {
  215. return fields.Nothing(), nil
  216. }
  217. selectors := []fields.Selector{}
  218. for _, expr := range nsm {
  219. switch expr.Operator {
  220. case v1.NodeSelectorOpIn:
  221. if len(expr.Values) != 1 {
  222. return nil, fmt.Errorf("unexpected number of value (%d) for node field selector operator %q",
  223. len(expr.Values), expr.Operator)
  224. }
  225. selectors = append(selectors, fields.OneTermEqualSelector(expr.Key, expr.Values[0]))
  226. case v1.NodeSelectorOpNotIn:
  227. if len(expr.Values) != 1 {
  228. return nil, fmt.Errorf("unexpected number of value (%d) for node field selector operator %q",
  229. len(expr.Values), expr.Operator)
  230. }
  231. selectors = append(selectors, fields.OneTermNotEqualSelector(expr.Key, expr.Values[0]))
  232. default:
  233. return nil, fmt.Errorf("%q is not a valid node field selector operator", expr.Operator)
  234. }
  235. }
  236. return fields.AndSelectors(selectors...), nil
  237. }
  238. // NodeSelectorRequirementKeysExistInNodeSelectorTerms checks if a NodeSelectorTerm with key is already specified in terms
  239. func NodeSelectorRequirementKeysExistInNodeSelectorTerms(reqs []v1.NodeSelectorRequirement, terms []v1.NodeSelectorTerm) bool {
  240. for _, req := range reqs {
  241. for _, term := range terms {
  242. for _, r := range term.MatchExpressions {
  243. if r.Key == req.Key {
  244. return true
  245. }
  246. }
  247. }
  248. }
  249. return false
  250. }
  251. // MatchNodeSelectorTerms checks whether the node labels and fields match node selector terms in ORed;
  252. // nil or empty term matches no objects.
  253. func MatchNodeSelectorTerms(
  254. nodeSelectorTerms []v1.NodeSelectorTerm,
  255. nodeLabels labels.Set,
  256. nodeFields fields.Set,
  257. ) bool {
  258. for _, req := range nodeSelectorTerms {
  259. // nil or empty term selects no objects
  260. if len(req.MatchExpressions) == 0 && len(req.MatchFields) == 0 {
  261. continue
  262. }
  263. if len(req.MatchExpressions) != 0 {
  264. labelSelector, err := NodeSelectorRequirementsAsSelector(req.MatchExpressions)
  265. if err != nil || !labelSelector.Matches(nodeLabels) {
  266. continue
  267. }
  268. }
  269. if len(req.MatchFields) != 0 {
  270. fieldSelector, err := NodeSelectorRequirementsAsFieldSelector(req.MatchFields)
  271. if err != nil || !fieldSelector.Matches(nodeFields) {
  272. continue
  273. }
  274. }
  275. return true
  276. }
  277. return false
  278. }
  279. // TopologySelectorRequirementsAsSelector converts the []TopologySelectorLabelRequirement api type into a struct
  280. // that implements labels.Selector.
  281. func TopologySelectorRequirementsAsSelector(tsm []v1.TopologySelectorLabelRequirement) (labels.Selector, error) {
  282. if len(tsm) == 0 {
  283. return labels.Nothing(), nil
  284. }
  285. selector := labels.NewSelector()
  286. for _, expr := range tsm {
  287. r, err := labels.NewRequirement(expr.Key, selection.In, expr.Values)
  288. if err != nil {
  289. return nil, err
  290. }
  291. selector = selector.Add(*r)
  292. }
  293. return selector, nil
  294. }
  295. // MatchTopologySelectorTerms checks whether given labels match topology selector terms in ORed;
  296. // nil or empty term matches no objects; while empty term list matches all objects.
  297. func MatchTopologySelectorTerms(topologySelectorTerms []v1.TopologySelectorTerm, lbls labels.Set) bool {
  298. if len(topologySelectorTerms) == 0 {
  299. // empty term list matches all objects
  300. return true
  301. }
  302. for _, req := range topologySelectorTerms {
  303. // nil or empty term selects no objects
  304. if len(req.MatchLabelExpressions) == 0 {
  305. continue
  306. }
  307. labelSelector, err := TopologySelectorRequirementsAsSelector(req.MatchLabelExpressions)
  308. if err != nil || !labelSelector.Matches(lbls) {
  309. continue
  310. }
  311. return true
  312. }
  313. return false
  314. }
  315. // AddOrUpdateTolerationInPodSpec tries to add a toleration to the toleration list in PodSpec.
  316. // Returns true if something was updated, false otherwise.
  317. func AddOrUpdateTolerationInPodSpec(spec *v1.PodSpec, toleration *v1.Toleration) bool {
  318. podTolerations := spec.Tolerations
  319. var newTolerations []v1.Toleration
  320. updated := false
  321. for i := range podTolerations {
  322. if toleration.MatchToleration(&podTolerations[i]) {
  323. if helper.Semantic.DeepEqual(toleration, podTolerations[i]) {
  324. return false
  325. }
  326. newTolerations = append(newTolerations, *toleration)
  327. updated = true
  328. continue
  329. }
  330. newTolerations = append(newTolerations, podTolerations[i])
  331. }
  332. if !updated {
  333. newTolerations = append(newTolerations, *toleration)
  334. }
  335. spec.Tolerations = newTolerations
  336. return true
  337. }
  338. // AddOrUpdateTolerationInPod tries to add a toleration to the pod's toleration list.
  339. // Returns true if something was updated, false otherwise.
  340. func AddOrUpdateTolerationInPod(pod *v1.Pod, toleration *v1.Toleration) bool {
  341. return AddOrUpdateTolerationInPodSpec(&pod.Spec, toleration)
  342. }
  343. // TolerationsTolerateTaint checks if taint is tolerated by any of the tolerations.
  344. func TolerationsTolerateTaint(tolerations []v1.Toleration, taint *v1.Taint) bool {
  345. for i := range tolerations {
  346. if tolerations[i].ToleratesTaint(taint) {
  347. return true
  348. }
  349. }
  350. return false
  351. }
  352. type taintsFilterFunc func(*v1.Taint) bool
  353. // TolerationsTolerateTaintsWithFilter checks if given tolerations tolerates
  354. // all the taints that apply to the filter in given taint list.
  355. func TolerationsTolerateTaintsWithFilter(tolerations []v1.Toleration, taints []v1.Taint, applyFilter taintsFilterFunc) bool {
  356. if len(taints) == 0 {
  357. return true
  358. }
  359. for i := range taints {
  360. if applyFilter != nil && !applyFilter(&taints[i]) {
  361. continue
  362. }
  363. if !TolerationsTolerateTaint(tolerations, &taints[i]) {
  364. return false
  365. }
  366. }
  367. return true
  368. }
  369. // Returns true and list of Tolerations matching all Taints if all are tolerated, or false otherwise.
  370. func GetMatchingTolerations(taints []v1.Taint, tolerations []v1.Toleration) (bool, []v1.Toleration) {
  371. if len(taints) == 0 {
  372. return true, []v1.Toleration{}
  373. }
  374. if len(tolerations) == 0 && len(taints) > 0 {
  375. return false, []v1.Toleration{}
  376. }
  377. result := []v1.Toleration{}
  378. for i := range taints {
  379. tolerated := false
  380. for j := range tolerations {
  381. if tolerations[j].ToleratesTaint(&taints[i]) {
  382. result = append(result, tolerations[j])
  383. tolerated = true
  384. break
  385. }
  386. }
  387. if !tolerated {
  388. return false, []v1.Toleration{}
  389. }
  390. }
  391. return true, result
  392. }
  393. func GetAvoidPodsFromNodeAnnotations(annotations map[string]string) (v1.AvoidPods, error) {
  394. var avoidPods v1.AvoidPods
  395. if len(annotations) > 0 && annotations[v1.PreferAvoidPodsAnnotationKey] != "" {
  396. err := json.Unmarshal([]byte(annotations[v1.PreferAvoidPodsAnnotationKey]), &avoidPods)
  397. if err != nil {
  398. return avoidPods, err
  399. }
  400. }
  401. return avoidPods, nil
  402. }
  403. // GetPersistentVolumeClass returns StorageClassName.
  404. func GetPersistentVolumeClass(volume *v1.PersistentVolume) string {
  405. // Use beta annotation first
  406. if class, found := volume.Annotations[v1.BetaStorageClassAnnotation]; found {
  407. return class
  408. }
  409. return volume.Spec.StorageClassName
  410. }
  411. // GetPersistentVolumeClaimClass returns StorageClassName. If no storage class was
  412. // requested, it returns "".
  413. func GetPersistentVolumeClaimClass(claim *v1.PersistentVolumeClaim) string {
  414. // Use beta annotation first
  415. if class, found := claim.Annotations[v1.BetaStorageClassAnnotation]; found {
  416. return class
  417. }
  418. if claim.Spec.StorageClassName != nil {
  419. return *claim.Spec.StorageClassName
  420. }
  421. return ""
  422. }
  423. // ScopedResourceSelectorRequirementsAsSelector converts the ScopedResourceSelectorRequirement api type into a struct that implements
  424. // labels.Selector.
  425. func ScopedResourceSelectorRequirementsAsSelector(ssr v1.ScopedResourceSelectorRequirement) (labels.Selector, error) {
  426. selector := labels.NewSelector()
  427. var op selection.Operator
  428. switch ssr.Operator {
  429. case v1.ScopeSelectorOpIn:
  430. op = selection.In
  431. case v1.ScopeSelectorOpNotIn:
  432. op = selection.NotIn
  433. case v1.ScopeSelectorOpExists:
  434. op = selection.Exists
  435. case v1.ScopeSelectorOpDoesNotExist:
  436. op = selection.DoesNotExist
  437. default:
  438. return nil, fmt.Errorf("%q is not a valid scope selector operator", ssr.Operator)
  439. }
  440. r, err := labels.NewRequirement(string(ssr.ScopeName), op, ssr.Values)
  441. if err != nil {
  442. return nil, err
  443. }
  444. selector = selector.Add(*r)
  445. return selector, nil
  446. }