helpers.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  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 v1
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "strings"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/labels"
  20. "k8s.io/apimachinery/pkg/selection"
  21. "k8s.io/apimachinery/pkg/types"
  22. "k8s.io/apimachinery/pkg/util/sets"
  23. "k8s.io/client-go/pkg/api"
  24. )
  25. // IsOpaqueIntResourceName returns true if the resource name has the opaque
  26. // integer resource prefix.
  27. func IsOpaqueIntResourceName(name ResourceName) bool {
  28. return strings.HasPrefix(string(name), ResourceOpaqueIntPrefix)
  29. }
  30. // OpaqueIntResourceName returns a ResourceName with the canonical opaque
  31. // integer prefix prepended. If the argument already has the prefix, it is
  32. // returned unmodified.
  33. func OpaqueIntResourceName(name string) ResourceName {
  34. if IsOpaqueIntResourceName(ResourceName(name)) {
  35. return ResourceName(name)
  36. }
  37. return ResourceName(fmt.Sprintf("%s%s", api.ResourceOpaqueIntPrefix, name))
  38. }
  39. // NewDeleteOptions returns a DeleteOptions indicating the resource should
  40. // be deleted within the specified grace period. Use zero to indicate
  41. // immediate deletion. If you would prefer to use the default grace period,
  42. // use &metav1.DeleteOptions{} directly.
  43. func NewDeleteOptions(grace int64) *DeleteOptions {
  44. return &DeleteOptions{GracePeriodSeconds: &grace}
  45. }
  46. // NewPreconditionDeleteOptions returns a DeleteOptions with a UID precondition set.
  47. func NewPreconditionDeleteOptions(uid string) *DeleteOptions {
  48. u := types.UID(uid)
  49. p := Preconditions{UID: &u}
  50. return &DeleteOptions{Preconditions: &p}
  51. }
  52. // NewUIDPreconditions returns a Preconditions with UID set.
  53. func NewUIDPreconditions(uid string) *Preconditions {
  54. u := types.UID(uid)
  55. return &Preconditions{UID: &u}
  56. }
  57. // this function aims to check if the service's ClusterIP is set or not
  58. // the objective is not to perform validation here
  59. func IsServiceIPSet(service *Service) bool {
  60. return service.Spec.ClusterIP != ClusterIPNone && service.Spec.ClusterIP != ""
  61. }
  62. // this function aims to check if the service's cluster IP is requested or not
  63. func IsServiceIPRequested(service *Service) bool {
  64. // ExternalName services are CNAME aliases to external ones. Ignore the IP.
  65. if service.Spec.Type == ServiceTypeExternalName {
  66. return false
  67. }
  68. return service.Spec.ClusterIP == ""
  69. }
  70. var standardFinalizers = sets.NewString(
  71. string(FinalizerKubernetes),
  72. metav1.FinalizerOrphanDependents,
  73. )
  74. func IsStandardFinalizerName(str string) bool {
  75. return standardFinalizers.Has(str)
  76. }
  77. // AddToNodeAddresses appends the NodeAddresses to the passed-by-pointer slice,
  78. // only if they do not already exist
  79. func AddToNodeAddresses(addresses *[]NodeAddress, addAddresses ...NodeAddress) {
  80. for _, add := range addAddresses {
  81. exists := false
  82. for _, existing := range *addresses {
  83. if existing.Address == add.Address && existing.Type == add.Type {
  84. exists = true
  85. break
  86. }
  87. }
  88. if !exists {
  89. *addresses = append(*addresses, add)
  90. }
  91. }
  92. }
  93. // TODO: make method on LoadBalancerStatus?
  94. func LoadBalancerStatusEqual(l, r *LoadBalancerStatus) bool {
  95. return ingressSliceEqual(l.Ingress, r.Ingress)
  96. }
  97. func ingressSliceEqual(lhs, rhs []LoadBalancerIngress) bool {
  98. if len(lhs) != len(rhs) {
  99. return false
  100. }
  101. for i := range lhs {
  102. if !ingressEqual(&lhs[i], &rhs[i]) {
  103. return false
  104. }
  105. }
  106. return true
  107. }
  108. func ingressEqual(lhs, rhs *LoadBalancerIngress) bool {
  109. if lhs.IP != rhs.IP {
  110. return false
  111. }
  112. if lhs.Hostname != rhs.Hostname {
  113. return false
  114. }
  115. return true
  116. }
  117. // TODO: make method on LoadBalancerStatus?
  118. func LoadBalancerStatusDeepCopy(lb *LoadBalancerStatus) *LoadBalancerStatus {
  119. c := &LoadBalancerStatus{}
  120. c.Ingress = make([]LoadBalancerIngress, len(lb.Ingress))
  121. for i := range lb.Ingress {
  122. c.Ingress[i] = lb.Ingress[i]
  123. }
  124. return c
  125. }
  126. // GetAccessModesAsString returns a string representation of an array of access modes.
  127. // modes, when present, are always in the same order: RWO,ROX,RWX.
  128. func GetAccessModesAsString(modes []PersistentVolumeAccessMode) string {
  129. modes = removeDuplicateAccessModes(modes)
  130. modesStr := []string{}
  131. if containsAccessMode(modes, ReadWriteOnce) {
  132. modesStr = append(modesStr, "RWO")
  133. }
  134. if containsAccessMode(modes, ReadOnlyMany) {
  135. modesStr = append(modesStr, "ROX")
  136. }
  137. if containsAccessMode(modes, ReadWriteMany) {
  138. modesStr = append(modesStr, "RWX")
  139. }
  140. return strings.Join(modesStr, ",")
  141. }
  142. // GetAccessModesAsString returns an array of AccessModes from a string created by GetAccessModesAsString
  143. func GetAccessModesFromString(modes string) []PersistentVolumeAccessMode {
  144. strmodes := strings.Split(modes, ",")
  145. accessModes := []PersistentVolumeAccessMode{}
  146. for _, s := range strmodes {
  147. s = strings.Trim(s, " ")
  148. switch {
  149. case s == "RWO":
  150. accessModes = append(accessModes, ReadWriteOnce)
  151. case s == "ROX":
  152. accessModes = append(accessModes, ReadOnlyMany)
  153. case s == "RWX":
  154. accessModes = append(accessModes, ReadWriteMany)
  155. }
  156. }
  157. return accessModes
  158. }
  159. // removeDuplicateAccessModes returns an array of access modes without any duplicates
  160. func removeDuplicateAccessModes(modes []PersistentVolumeAccessMode) []PersistentVolumeAccessMode {
  161. accessModes := []PersistentVolumeAccessMode{}
  162. for _, m := range modes {
  163. if !containsAccessMode(accessModes, m) {
  164. accessModes = append(accessModes, m)
  165. }
  166. }
  167. return accessModes
  168. }
  169. func containsAccessMode(modes []PersistentVolumeAccessMode, mode PersistentVolumeAccessMode) bool {
  170. for _, m := range modes {
  171. if m == mode {
  172. return true
  173. }
  174. }
  175. return false
  176. }
  177. // NodeSelectorRequirementsAsSelector converts the []NodeSelectorRequirement api type into a struct that implements
  178. // labels.Selector.
  179. func NodeSelectorRequirementsAsSelector(nsm []NodeSelectorRequirement) (labels.Selector, error) {
  180. if len(nsm) == 0 {
  181. return labels.Nothing(), nil
  182. }
  183. selector := labels.NewSelector()
  184. for _, expr := range nsm {
  185. var op selection.Operator
  186. switch expr.Operator {
  187. case NodeSelectorOpIn:
  188. op = selection.In
  189. case NodeSelectorOpNotIn:
  190. op = selection.NotIn
  191. case NodeSelectorOpExists:
  192. op = selection.Exists
  193. case NodeSelectorOpDoesNotExist:
  194. op = selection.DoesNotExist
  195. case NodeSelectorOpGt:
  196. op = selection.GreaterThan
  197. case NodeSelectorOpLt:
  198. op = selection.LessThan
  199. default:
  200. return nil, fmt.Errorf("%q is not a valid node selector operator", expr.Operator)
  201. }
  202. r, err := labels.NewRequirement(expr.Key, op, expr.Values)
  203. if err != nil {
  204. return nil, err
  205. }
  206. selector = selector.Add(*r)
  207. }
  208. return selector, nil
  209. }
  210. const (
  211. // SeccompPodAnnotationKey represents the key of a seccomp profile applied
  212. // to all containers of a pod.
  213. SeccompPodAnnotationKey string = "seccomp.security.alpha.kubernetes.io/pod"
  214. // SeccompContainerAnnotationKeyPrefix represents the key of a seccomp profile applied
  215. // to one container of a pod.
  216. SeccompContainerAnnotationKeyPrefix string = "container.seccomp.security.alpha.kubernetes.io/"
  217. // CreatedByAnnotation represents the key used to store the spec(json)
  218. // used to create the resource.
  219. CreatedByAnnotation = "kubernetes.io/created-by"
  220. // PreferAvoidPodsAnnotationKey represents the key of preferAvoidPods data (json serialized)
  221. // in the Annotations of a Node.
  222. PreferAvoidPodsAnnotationKey string = "scheduler.alpha.kubernetes.io/preferAvoidPods"
  223. // SysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
  224. // container of a pod. The annotation value is a comma separated list of sysctl_name=value
  225. // key-value pairs. Only a limited set of whitelisted and isolated sysctls is supported by
  226. // the kubelet. Pods with other sysctls will fail to launch.
  227. SysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/sysctls"
  228. // UnsafeSysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
  229. // container of a pod. The annotation value is a comma separated list of sysctl_name=value
  230. // key-value pairs. Unsafe sysctls must be explicitly enabled for a kubelet. They are properly
  231. // namespaced to a pod or a container, but their isolation is usually unclear or weak. Their use
  232. // is at-your-own-risk. Pods that attempt to set an unsafe sysctl that is not enabled for a kubelet
  233. // will fail to launch.
  234. UnsafeSysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/unsafe-sysctls"
  235. // ObjectTTLAnnotations represents a suggestion for kubelet for how long it can cache
  236. // an object (e.g. secret, config map) before fetching it again from apiserver.
  237. // This annotation can be attached to node.
  238. ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl"
  239. // AffinityAnnotationKey represents the key of affinity data (json serialized)
  240. // in the Annotations of a Pod.
  241. // TODO: remove when alpha support for affinity is removed
  242. AffinityAnnotationKey string = "scheduler.alpha.kubernetes.io/affinity"
  243. )
  244. // AddOrUpdateTolerationInPodSpec tries to add a toleration to the toleration list in PodSpec.
  245. // Returns true if something was updated, false otherwise.
  246. func AddOrUpdateTolerationInPodSpec(spec *PodSpec, toleration *Toleration) (bool, error) {
  247. podTolerations := spec.Tolerations
  248. var newTolerations []Toleration
  249. updated := false
  250. for i := range podTolerations {
  251. if toleration.MatchToleration(&podTolerations[i]) {
  252. if api.Semantic.DeepEqual(toleration, podTolerations[i]) {
  253. return false, nil
  254. }
  255. newTolerations = append(newTolerations, *toleration)
  256. updated = true
  257. continue
  258. }
  259. newTolerations = append(newTolerations, podTolerations[i])
  260. }
  261. if !updated {
  262. newTolerations = append(newTolerations, *toleration)
  263. }
  264. spec.Tolerations = newTolerations
  265. return true, nil
  266. }
  267. // AddOrUpdateTolerationInPod tries to add a toleration to the pod's toleration list.
  268. // Returns true if something was updated, false otherwise.
  269. func AddOrUpdateTolerationInPod(pod *Pod, toleration *Toleration) (bool, error) {
  270. return AddOrUpdateTolerationInPodSpec(&pod.Spec, toleration)
  271. }
  272. // MatchToleration checks if the toleration matches tolerationToMatch. Tolerations are unique by <key,effect,operator,value>,
  273. // if the two tolerations have same <key,effect,operator,value> combination, regard as they match.
  274. // TODO: uniqueness check for tolerations in api validations.
  275. func (t *Toleration) MatchToleration(tolerationToMatch *Toleration) bool {
  276. return t.Key == tolerationToMatch.Key &&
  277. t.Effect == tolerationToMatch.Effect &&
  278. t.Operator == tolerationToMatch.Operator &&
  279. t.Value == tolerationToMatch.Value
  280. }
  281. // ToleratesTaint checks if the toleration tolerates the taint.
  282. // The matching follows the rules below:
  283. // (1) Empty toleration.effect means to match all taint effects,
  284. // otherwise taint effect must equal to toleration.effect.
  285. // (2) If toleration.operator is 'Exists', it means to match all taint values.
  286. // (3) Empty toleration.key means to match all taint keys.
  287. // If toleration.key is empty, toleration.operator must be 'Exists';
  288. // this combination means to match all taint values and all taint keys.
  289. func (t *Toleration) ToleratesTaint(taint *Taint) bool {
  290. if len(t.Effect) > 0 && t.Effect != taint.Effect {
  291. return false
  292. }
  293. if len(t.Key) > 0 && t.Key != taint.Key {
  294. return false
  295. }
  296. // TODO: Use proper defaulting when Toleration becomes a field of PodSpec
  297. switch t.Operator {
  298. // empty operator means Equal
  299. case "", TolerationOpEqual:
  300. return t.Value == taint.Value
  301. case TolerationOpExists:
  302. return true
  303. default:
  304. return false
  305. }
  306. }
  307. // TolerationsTolerateTaint checks if taint is tolerated by any of the tolerations.
  308. func TolerationsTolerateTaint(tolerations []Toleration, taint *Taint) bool {
  309. for i := range tolerations {
  310. if tolerations[i].ToleratesTaint(taint) {
  311. return true
  312. }
  313. }
  314. return false
  315. }
  316. type taintsFilterFunc func(*Taint) bool
  317. // TolerationsTolerateTaintsWithFilter checks if given tolerations tolerates
  318. // all the taints that apply to the filter in given taint list.
  319. func TolerationsTolerateTaintsWithFilter(tolerations []Toleration, taints []Taint, applyFilter taintsFilterFunc) bool {
  320. if len(taints) == 0 {
  321. return true
  322. }
  323. for i := range taints {
  324. if applyFilter != nil && !applyFilter(&taints[i]) {
  325. continue
  326. }
  327. if !TolerationsTolerateTaint(tolerations, &taints[i]) {
  328. return false
  329. }
  330. }
  331. return true
  332. }
  333. // DeleteTaintsByKey removes all the taints that have the same key to given taintKey
  334. func DeleteTaintsByKey(taints []Taint, taintKey string) ([]Taint, bool) {
  335. newTaints := []Taint{}
  336. deleted := false
  337. for i := range taints {
  338. if taintKey == taints[i].Key {
  339. deleted = true
  340. continue
  341. }
  342. newTaints = append(newTaints, taints[i])
  343. }
  344. return newTaints, deleted
  345. }
  346. // DeleteTaint removes all the the taints that have the same key and effect to given taintToDelete.
  347. func DeleteTaint(taints []Taint, taintToDelete *Taint) ([]Taint, bool) {
  348. newTaints := []Taint{}
  349. deleted := false
  350. for i := range taints {
  351. if taintToDelete.MatchTaint(&taints[i]) {
  352. deleted = true
  353. continue
  354. }
  355. newTaints = append(newTaints, taints[i])
  356. }
  357. return newTaints, deleted
  358. }
  359. // Returns true and list of Tolerations matching all Taints if all are tolerated, or false otherwise.
  360. func GetMatchingTolerations(taints []Taint, tolerations []Toleration) (bool, []Toleration) {
  361. if len(taints) == 0 {
  362. return true, []Toleration{}
  363. }
  364. if len(tolerations) == 0 && len(taints) > 0 {
  365. return false, []Toleration{}
  366. }
  367. result := []Toleration{}
  368. for i := range taints {
  369. tolerated := false
  370. for j := range tolerations {
  371. if tolerations[j].ToleratesTaint(&taints[i]) {
  372. result = append(result, tolerations[j])
  373. tolerated = true
  374. break
  375. }
  376. }
  377. if !tolerated {
  378. return false, []Toleration{}
  379. }
  380. }
  381. return true, result
  382. }
  383. // MatchTaint checks if the taint matches taintToMatch. Taints are unique by key:effect,
  384. // if the two taints have same key:effect, regard as they match.
  385. func (t *Taint) MatchTaint(taintToMatch *Taint) bool {
  386. return t.Key == taintToMatch.Key && t.Effect == taintToMatch.Effect
  387. }
  388. // taint.ToString() converts taint struct to string in format key=value:effect or key:effect.
  389. func (t *Taint) ToString() string {
  390. if len(t.Value) == 0 {
  391. return fmt.Sprintf("%v:%v", t.Key, t.Effect)
  392. }
  393. return fmt.Sprintf("%v=%v:%v", t.Key, t.Value, t.Effect)
  394. }
  395. func GetAvoidPodsFromNodeAnnotations(annotations map[string]string) (AvoidPods, error) {
  396. var avoidPods AvoidPods
  397. if len(annotations) > 0 && annotations[PreferAvoidPodsAnnotationKey] != "" {
  398. err := json.Unmarshal([]byte(annotations[PreferAvoidPodsAnnotationKey]), &avoidPods)
  399. if err != nil {
  400. return avoidPods, err
  401. }
  402. }
  403. return avoidPods, nil
  404. }
  405. // SysctlsFromPodAnnotations parses the sysctl annotations into a slice of safe Sysctls
  406. // and a slice of unsafe Sysctls. This is only a convenience wrapper around
  407. // SysctlsFromPodAnnotation.
  408. func SysctlsFromPodAnnotations(a map[string]string) ([]Sysctl, []Sysctl, error) {
  409. safe, err := SysctlsFromPodAnnotation(a[SysctlsPodAnnotationKey])
  410. if err != nil {
  411. return nil, nil, err
  412. }
  413. unsafe, err := SysctlsFromPodAnnotation(a[UnsafeSysctlsPodAnnotationKey])
  414. if err != nil {
  415. return nil, nil, err
  416. }
  417. return safe, unsafe, nil
  418. }
  419. // SysctlsFromPodAnnotation parses an annotation value into a slice of Sysctls.
  420. func SysctlsFromPodAnnotation(annotation string) ([]Sysctl, error) {
  421. if len(annotation) == 0 {
  422. return nil, nil
  423. }
  424. kvs := strings.Split(annotation, ",")
  425. sysctls := make([]Sysctl, len(kvs))
  426. for i, kv := range kvs {
  427. cs := strings.Split(kv, "=")
  428. if len(cs) != 2 || len(cs[0]) == 0 {
  429. return nil, fmt.Errorf("sysctl %q not of the format sysctl_name=value", kv)
  430. }
  431. sysctls[i].Name = cs[0]
  432. sysctls[i].Value = cs[1]
  433. }
  434. return sysctls, nil
  435. }
  436. // PodAnnotationsFromSysctls creates an annotation value for a slice of Sysctls.
  437. func PodAnnotationsFromSysctls(sysctls []Sysctl) string {
  438. if len(sysctls) == 0 {
  439. return ""
  440. }
  441. kvs := make([]string, len(sysctls))
  442. for i := range sysctls {
  443. kvs[i] = fmt.Sprintf("%s=%s", sysctls[i].Name, sysctls[i].Value)
  444. }
  445. return strings.Join(kvs, ",")
  446. }
  447. type Sysctl struct {
  448. Name string `protobuf:"bytes,1,opt,name=name"`
  449. Value string `protobuf:"bytes,2,opt,name=value"`
  450. }
  451. // NodeResources is an object for conveying resource information about a node.
  452. // see http://releases.k8s.io/HEAD/docs/design/resources.md for more details.
  453. type NodeResources struct {
  454. // Capacity represents the available resources of a node
  455. Capacity ResourceList `protobuf:"bytes,1,rep,name=capacity,casttype=ResourceList,castkey=ResourceName"`
  456. }
  457. // Tries to add a taint to annotations list. Returns a new copy of updated Node and true if something was updated
  458. // false otherwise.
  459. func AddOrUpdateTaint(node *Node, taint *Taint) (*Node, bool, error) {
  460. objCopy, err := api.Scheme.DeepCopy(node)
  461. if err != nil {
  462. return nil, false, err
  463. }
  464. newNode := objCopy.(*Node)
  465. nodeTaints := newNode.Spec.Taints
  466. var newTaints []Taint
  467. updated := false
  468. for i := range nodeTaints {
  469. if taint.MatchTaint(&nodeTaints[i]) {
  470. if api.Semantic.DeepEqual(taint, nodeTaints[i]) {
  471. return newNode, false, nil
  472. }
  473. newTaints = append(newTaints, *taint)
  474. updated = true
  475. continue
  476. }
  477. newTaints = append(newTaints, nodeTaints[i])
  478. }
  479. if !updated {
  480. newTaints = append(newTaints, *taint)
  481. }
  482. newNode.Spec.Taints = newTaints
  483. return newNode, true, nil
  484. }
  485. func TaintExists(taints []Taint, taintToFind *Taint) bool {
  486. for _, taint := range taints {
  487. if taint.MatchTaint(taintToFind) {
  488. return true
  489. }
  490. }
  491. return false
  492. }
  493. // Tries to remove a taint from annotations list. Returns a new copy of updated Node and true if something was updated
  494. // false otherwise.
  495. func RemoveTaint(node *Node, taint *Taint) (*Node, bool, error) {
  496. objCopy, err := api.Scheme.DeepCopy(node)
  497. if err != nil {
  498. return nil, false, err
  499. }
  500. newNode := objCopy.(*Node)
  501. nodeTaints := newNode.Spec.Taints
  502. if len(nodeTaints) == 0 {
  503. return newNode, false, nil
  504. }
  505. if !TaintExists(nodeTaints, taint) {
  506. return newNode, false, nil
  507. }
  508. newTaints, _ := DeleteTaint(nodeTaints, taint)
  509. newNode.Spec.Taints = newTaints
  510. return newNode, true, nil
  511. }
  512. // GetAffinityFromPodAnnotations gets the json serialized affinity data from Pod.Annotations
  513. // and converts it to the Affinity type in api.
  514. // TODO: remove when alpha support for affinity is removed
  515. func GetAffinityFromPodAnnotations(annotations map[string]string) (*Affinity, error) {
  516. if len(annotations) > 0 && annotations[AffinityAnnotationKey] != "" {
  517. var affinity Affinity
  518. err := json.Unmarshal([]byte(annotations[AffinityAnnotationKey]), &affinity)
  519. if err != nil {
  520. return nil, err
  521. }
  522. return &affinity, nil
  523. }
  524. return nil, nil
  525. }
  526. // GetPersistentVolumeClass returns StorageClassName.
  527. func GetPersistentVolumeClass(volume *PersistentVolume) string {
  528. // Use beta annotation first
  529. if class, found := volume.Annotations[BetaStorageClassAnnotation]; found {
  530. return class
  531. }
  532. return volume.Spec.StorageClassName
  533. }
  534. // GetPersistentVolumeClaimClass returns StorageClassName. If no storage class was
  535. // requested, it returns "".
  536. func GetPersistentVolumeClaimClass(claim *PersistentVolumeClaim) string {
  537. // Use beta annotation first
  538. if class, found := claim.Annotations[BetaStorageClassAnnotation]; found {
  539. return class
  540. }
  541. if claim.Spec.StorageClassName != nil {
  542. return *claim.Spec.StorageClassName
  543. }
  544. return ""
  545. }
  546. // PersistentVolumeClaimHasClass returns true if given claim has set StorageClassName field.
  547. func PersistentVolumeClaimHasClass(claim *PersistentVolumeClaim) bool {
  548. // Use beta annotation first
  549. if _, found := claim.Annotations[BetaStorageClassAnnotation]; found {
  550. return true
  551. }
  552. if claim.Spec.StorageClassName != nil {
  553. return true
  554. }
  555. return false
  556. }