helpers.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  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 api
  14. import (
  15. "crypto/md5"
  16. "encoding/json"
  17. "fmt"
  18. "reflect"
  19. "strings"
  20. "time"
  21. "github.com/davecgh/go-spew/spew"
  22. "k8s.io/apimachinery/pkg/api/resource"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/conversion"
  25. "k8s.io/apimachinery/pkg/fields"
  26. "k8s.io/apimachinery/pkg/labels"
  27. "k8s.io/apimachinery/pkg/runtime"
  28. "k8s.io/apimachinery/pkg/selection"
  29. "k8s.io/apimachinery/pkg/util/sets"
  30. )
  31. // Conversion error conveniently packages up errors in conversions.
  32. type ConversionError struct {
  33. In, Out interface{}
  34. Message string
  35. }
  36. // Return a helpful string about the error
  37. func (c *ConversionError) Error() string {
  38. return spew.Sprintf(
  39. "Conversion error: %s. (in: %v(%+v) out: %v)",
  40. c.Message, reflect.TypeOf(c.In), c.In, reflect.TypeOf(c.Out),
  41. )
  42. }
  43. const (
  44. // annotation key prefix used to identify non-convertible json paths.
  45. NonConvertibleAnnotationPrefix = "non-convertible.kubernetes.io"
  46. )
  47. // NonConvertibleFields iterates over the provided map and filters out all but
  48. // any keys with the "non-convertible.kubernetes.io" prefix.
  49. func NonConvertibleFields(annotations map[string]string) map[string]string {
  50. nonConvertibleKeys := map[string]string{}
  51. for key, value := range annotations {
  52. if strings.HasPrefix(key, NonConvertibleAnnotationPrefix) {
  53. nonConvertibleKeys[key] = value
  54. }
  55. }
  56. return nonConvertibleKeys
  57. }
  58. // Semantic can do semantic deep equality checks for api objects.
  59. // Example: apiequality.Semantic.DeepEqual(aPod, aPodWithNonNilButEmptyMaps) == true
  60. var Semantic = conversion.EqualitiesOrDie(
  61. func(a, b resource.Quantity) bool {
  62. // Ignore formatting, only care that numeric value stayed the same.
  63. // TODO: if we decide it's important, it should be safe to start comparing the format.
  64. //
  65. // Uninitialized quantities are equivalent to 0 quantities.
  66. return a.Cmp(b) == 0
  67. },
  68. func(a, b metav1.Time) bool {
  69. return a.UTC() == b.UTC()
  70. },
  71. func(a, b labels.Selector) bool {
  72. return a.String() == b.String()
  73. },
  74. func(a, b fields.Selector) bool {
  75. return a.String() == b.String()
  76. },
  77. )
  78. var standardResourceQuotaScopes = sets.NewString(
  79. string(ResourceQuotaScopeTerminating),
  80. string(ResourceQuotaScopeNotTerminating),
  81. string(ResourceQuotaScopeBestEffort),
  82. string(ResourceQuotaScopeNotBestEffort),
  83. )
  84. // IsStandardResourceQuotaScope returns true if the scope is a standard value
  85. func IsStandardResourceQuotaScope(str string) bool {
  86. return standardResourceQuotaScopes.Has(str)
  87. }
  88. var podObjectCountQuotaResources = sets.NewString(
  89. string(ResourcePods),
  90. )
  91. var podComputeQuotaResources = sets.NewString(
  92. string(ResourceCPU),
  93. string(ResourceMemory),
  94. string(ResourceLimitsCPU),
  95. string(ResourceLimitsMemory),
  96. string(ResourceRequestsCPU),
  97. string(ResourceRequestsMemory),
  98. )
  99. // IsResourceQuotaScopeValidForResource returns true if the resource applies to the specified scope
  100. func IsResourceQuotaScopeValidForResource(scope ResourceQuotaScope, resource string) bool {
  101. switch scope {
  102. case ResourceQuotaScopeTerminating, ResourceQuotaScopeNotTerminating, ResourceQuotaScopeNotBestEffort:
  103. return podObjectCountQuotaResources.Has(resource) || podComputeQuotaResources.Has(resource)
  104. case ResourceQuotaScopeBestEffort:
  105. return podObjectCountQuotaResources.Has(resource)
  106. default:
  107. return true
  108. }
  109. }
  110. var standardContainerResources = sets.NewString(
  111. string(ResourceCPU),
  112. string(ResourceMemory),
  113. )
  114. // IsStandardContainerResourceName returns true if the container can make a resource request
  115. // for the specified resource
  116. func IsStandardContainerResourceName(str string) bool {
  117. return standardContainerResources.Has(str)
  118. }
  119. // IsOpaqueIntResourceName returns true if the resource name has the opaque
  120. // integer resource prefix.
  121. func IsOpaqueIntResourceName(name ResourceName) bool {
  122. return strings.HasPrefix(string(name), ResourceOpaqueIntPrefix)
  123. }
  124. // OpaqueIntResourceName returns a ResourceName with the canonical opaque
  125. // integer prefix prepended. If the argument already has the prefix, it is
  126. // returned unmodified.
  127. func OpaqueIntResourceName(name string) ResourceName {
  128. if IsOpaqueIntResourceName(ResourceName(name)) {
  129. return ResourceName(name)
  130. }
  131. return ResourceName(fmt.Sprintf("%s%s", ResourceOpaqueIntPrefix, name))
  132. }
  133. var standardLimitRangeTypes = sets.NewString(
  134. string(LimitTypePod),
  135. string(LimitTypeContainer),
  136. string(LimitTypePersistentVolumeClaim),
  137. )
  138. // IsStandardLimitRangeType returns true if the type is Pod or Container
  139. func IsStandardLimitRangeType(str string) bool {
  140. return standardLimitRangeTypes.Has(str)
  141. }
  142. var standardQuotaResources = sets.NewString(
  143. string(ResourceCPU),
  144. string(ResourceMemory),
  145. string(ResourceRequestsCPU),
  146. string(ResourceRequestsMemory),
  147. string(ResourceRequestsStorage),
  148. string(ResourceLimitsCPU),
  149. string(ResourceLimitsMemory),
  150. string(ResourcePods),
  151. string(ResourceQuotas),
  152. string(ResourceServices),
  153. string(ResourceReplicationControllers),
  154. string(ResourceSecrets),
  155. string(ResourcePersistentVolumeClaims),
  156. string(ResourceConfigMaps),
  157. string(ResourceServicesNodePorts),
  158. string(ResourceServicesLoadBalancers),
  159. )
  160. // IsStandardQuotaResourceName returns true if the resource is known to
  161. // the quota tracking system
  162. func IsStandardQuotaResourceName(str string) bool {
  163. return standardQuotaResources.Has(str)
  164. }
  165. var standardResources = sets.NewString(
  166. string(ResourceCPU),
  167. string(ResourceMemory),
  168. string(ResourceRequestsCPU),
  169. string(ResourceRequestsMemory),
  170. string(ResourceLimitsCPU),
  171. string(ResourceLimitsMemory),
  172. string(ResourcePods),
  173. string(ResourceQuotas),
  174. string(ResourceServices),
  175. string(ResourceReplicationControllers),
  176. string(ResourceSecrets),
  177. string(ResourceConfigMaps),
  178. string(ResourcePersistentVolumeClaims),
  179. string(ResourceStorage),
  180. string(ResourceRequestsStorage),
  181. )
  182. // IsStandardResourceName returns true if the resource is known to the system
  183. func IsStandardResourceName(str string) bool {
  184. return standardResources.Has(str)
  185. }
  186. var integerResources = sets.NewString(
  187. string(ResourcePods),
  188. string(ResourceQuotas),
  189. string(ResourceServices),
  190. string(ResourceReplicationControllers),
  191. string(ResourceSecrets),
  192. string(ResourceConfigMaps),
  193. string(ResourcePersistentVolumeClaims),
  194. string(ResourceServicesNodePorts),
  195. string(ResourceServicesLoadBalancers),
  196. )
  197. // IsIntegerResourceName returns true if the resource is measured in integer values
  198. func IsIntegerResourceName(str string) bool {
  199. return integerResources.Has(str) || IsOpaqueIntResourceName(ResourceName(str))
  200. }
  201. // this function aims to check if the service's ClusterIP is set or not
  202. // the objective is not to perform validation here
  203. func IsServiceIPSet(service *Service) bool {
  204. return service.Spec.ClusterIP != ClusterIPNone && service.Spec.ClusterIP != ""
  205. }
  206. // this function aims to check if the service's cluster IP is requested or not
  207. func IsServiceIPRequested(service *Service) bool {
  208. // ExternalName services are CNAME aliases to external ones. Ignore the IP.
  209. if service.Spec.Type == ServiceTypeExternalName {
  210. return false
  211. }
  212. return service.Spec.ClusterIP == ""
  213. }
  214. var standardFinalizers = sets.NewString(
  215. string(FinalizerKubernetes),
  216. metav1.FinalizerOrphanDependents,
  217. )
  218. // HasAnnotation returns a bool if passed in annotation exists
  219. func HasAnnotation(obj ObjectMeta, ann string) bool {
  220. _, found := obj.Annotations[ann]
  221. return found
  222. }
  223. // SetMetaDataAnnotation sets the annotation and value
  224. func SetMetaDataAnnotation(obj *ObjectMeta, ann string, value string) {
  225. if obj.Annotations == nil {
  226. obj.Annotations = make(map[string]string)
  227. }
  228. obj.Annotations[ann] = value
  229. }
  230. func IsStandardFinalizerName(str string) bool {
  231. return standardFinalizers.Has(str)
  232. }
  233. // AddToNodeAddresses appends the NodeAddresses to the passed-by-pointer slice,
  234. // only if they do not already exist
  235. func AddToNodeAddresses(addresses *[]NodeAddress, addAddresses ...NodeAddress) {
  236. for _, add := range addAddresses {
  237. exists := false
  238. for _, existing := range *addresses {
  239. if existing.Address == add.Address && existing.Type == add.Type {
  240. exists = true
  241. break
  242. }
  243. }
  244. if !exists {
  245. *addresses = append(*addresses, add)
  246. }
  247. }
  248. }
  249. func HashObject(obj runtime.Object, codec runtime.Codec) (string, error) {
  250. data, err := runtime.Encode(codec, obj)
  251. if err != nil {
  252. return "", err
  253. }
  254. return fmt.Sprintf("%x", md5.Sum(data)), nil
  255. }
  256. // TODO: make method on LoadBalancerStatus?
  257. func LoadBalancerStatusEqual(l, r *LoadBalancerStatus) bool {
  258. return ingressSliceEqual(l.Ingress, r.Ingress)
  259. }
  260. func ingressSliceEqual(lhs, rhs []LoadBalancerIngress) bool {
  261. if len(lhs) != len(rhs) {
  262. return false
  263. }
  264. for i := range lhs {
  265. if !ingressEqual(&lhs[i], &rhs[i]) {
  266. return false
  267. }
  268. }
  269. return true
  270. }
  271. func ingressEqual(lhs, rhs *LoadBalancerIngress) bool {
  272. if lhs.IP != rhs.IP {
  273. return false
  274. }
  275. if lhs.Hostname != rhs.Hostname {
  276. return false
  277. }
  278. return true
  279. }
  280. // TODO: make method on LoadBalancerStatus?
  281. func LoadBalancerStatusDeepCopy(lb *LoadBalancerStatus) *LoadBalancerStatus {
  282. c := &LoadBalancerStatus{}
  283. c.Ingress = make([]LoadBalancerIngress, len(lb.Ingress))
  284. for i := range lb.Ingress {
  285. c.Ingress[i] = lb.Ingress[i]
  286. }
  287. return c
  288. }
  289. // GetAccessModesAsString returns a string representation of an array of access modes.
  290. // modes, when present, are always in the same order: RWO,ROX,RWX.
  291. func GetAccessModesAsString(modes []PersistentVolumeAccessMode) string {
  292. modes = removeDuplicateAccessModes(modes)
  293. modesStr := []string{}
  294. if containsAccessMode(modes, ReadWriteOnce) {
  295. modesStr = append(modesStr, "RWO")
  296. }
  297. if containsAccessMode(modes, ReadOnlyMany) {
  298. modesStr = append(modesStr, "ROX")
  299. }
  300. if containsAccessMode(modes, ReadWriteMany) {
  301. modesStr = append(modesStr, "RWX")
  302. }
  303. return strings.Join(modesStr, ",")
  304. }
  305. // GetAccessModesAsString returns an array of AccessModes from a string created by GetAccessModesAsString
  306. func GetAccessModesFromString(modes string) []PersistentVolumeAccessMode {
  307. strmodes := strings.Split(modes, ",")
  308. accessModes := []PersistentVolumeAccessMode{}
  309. for _, s := range strmodes {
  310. s = strings.Trim(s, " ")
  311. switch {
  312. case s == "RWO":
  313. accessModes = append(accessModes, ReadWriteOnce)
  314. case s == "ROX":
  315. accessModes = append(accessModes, ReadOnlyMany)
  316. case s == "RWX":
  317. accessModes = append(accessModes, ReadWriteMany)
  318. }
  319. }
  320. return accessModes
  321. }
  322. // removeDuplicateAccessModes returns an array of access modes without any duplicates
  323. func removeDuplicateAccessModes(modes []PersistentVolumeAccessMode) []PersistentVolumeAccessMode {
  324. accessModes := []PersistentVolumeAccessMode{}
  325. for _, m := range modes {
  326. if !containsAccessMode(accessModes, m) {
  327. accessModes = append(accessModes, m)
  328. }
  329. }
  330. return accessModes
  331. }
  332. func containsAccessMode(modes []PersistentVolumeAccessMode, mode PersistentVolumeAccessMode) bool {
  333. for _, m := range modes {
  334. if m == mode {
  335. return true
  336. }
  337. }
  338. return false
  339. }
  340. // ParseRFC3339 parses an RFC3339 date in either RFC3339Nano or RFC3339 format.
  341. func ParseRFC3339(s string, nowFn func() metav1.Time) (metav1.Time, error) {
  342. if t, timeErr := time.Parse(time.RFC3339Nano, s); timeErr == nil {
  343. return metav1.Time{Time: t}, nil
  344. }
  345. t, err := time.Parse(time.RFC3339, s)
  346. if err != nil {
  347. return metav1.Time{}, err
  348. }
  349. return metav1.Time{Time: t}, nil
  350. }
  351. // NodeSelectorRequirementsAsSelector converts the []NodeSelectorRequirement api type into a struct that implements
  352. // labels.Selector.
  353. func NodeSelectorRequirementsAsSelector(nsm []NodeSelectorRequirement) (labels.Selector, error) {
  354. if len(nsm) == 0 {
  355. return labels.Nothing(), nil
  356. }
  357. selector := labels.NewSelector()
  358. for _, expr := range nsm {
  359. var op selection.Operator
  360. switch expr.Operator {
  361. case NodeSelectorOpIn:
  362. op = selection.In
  363. case NodeSelectorOpNotIn:
  364. op = selection.NotIn
  365. case NodeSelectorOpExists:
  366. op = selection.Exists
  367. case NodeSelectorOpDoesNotExist:
  368. op = selection.DoesNotExist
  369. case NodeSelectorOpGt:
  370. op = selection.GreaterThan
  371. case NodeSelectorOpLt:
  372. op = selection.LessThan
  373. default:
  374. return nil, fmt.Errorf("%q is not a valid node selector operator", expr.Operator)
  375. }
  376. r, err := labels.NewRequirement(expr.Key, op, expr.Values)
  377. if err != nil {
  378. return nil, err
  379. }
  380. selector = selector.Add(*r)
  381. }
  382. return selector, nil
  383. }
  384. const (
  385. // TolerationsAnnotationKey represents the key of tolerations data (json serialized)
  386. // in the Annotations of a Pod.
  387. TolerationsAnnotationKey string = "scheduler.alpha.kubernetes.io/tolerations"
  388. // TaintsAnnotationKey represents the key of taints data (json serialized)
  389. // in the Annotations of a Node.
  390. TaintsAnnotationKey string = "scheduler.alpha.kubernetes.io/taints"
  391. // SeccompPodAnnotationKey represents the key of a seccomp profile applied
  392. // to all containers of a pod.
  393. SeccompPodAnnotationKey string = "seccomp.security.alpha.kubernetes.io/pod"
  394. // SeccompContainerAnnotationKeyPrefix represents the key of a seccomp profile applied
  395. // to one container of a pod.
  396. SeccompContainerAnnotationKeyPrefix string = "container.seccomp.security.alpha.kubernetes.io/"
  397. // CreatedByAnnotation represents the key used to store the spec(json)
  398. // used to create the resource.
  399. CreatedByAnnotation = "kubernetes.io/created-by"
  400. // PreferAvoidPodsAnnotationKey represents the key of preferAvoidPods data (json serialized)
  401. // in the Annotations of a Node.
  402. PreferAvoidPodsAnnotationKey string = "scheduler.alpha.kubernetes.io/preferAvoidPods"
  403. // SysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
  404. // container of a pod. The annotation value is a comma separated list of sysctl_name=value
  405. // key-value pairs. Only a limited set of whitelisted and isolated sysctls is supported by
  406. // the kubelet. Pods with other sysctls will fail to launch.
  407. SysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/sysctls"
  408. // UnsafeSysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
  409. // container of a pod. The annotation value is a comma separated list of sysctl_name=value
  410. // key-value pairs. Unsafe sysctls must be explicitly enabled for a kubelet. They are properly
  411. // namespaced to a pod or a container, but their isolation is usually unclear or weak. Their use
  412. // is at-your-own-risk. Pods that attempt to set an unsafe sysctl that is not enabled for a kubelet
  413. // will fail to launch.
  414. UnsafeSysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/unsafe-sysctls"
  415. // ObjectTTLAnnotations represents a suggestion for kubelet for how long it can cache
  416. // an object (e.g. secret, config map) before fetching it again from apiserver.
  417. // This annotation can be attached to node.
  418. ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl"
  419. // AffinityAnnotationKey represents the key of affinity data (json serialized)
  420. // in the Annotations of a Pod.
  421. // TODO: remove when alpha support for affinity is removed
  422. AffinityAnnotationKey string = "scheduler.alpha.kubernetes.io/affinity"
  423. )
  424. // GetTolerationsFromPodAnnotations gets the json serialized tolerations data from Pod.Annotations
  425. // and converts it to the []Toleration type in api.
  426. func GetTolerationsFromPodAnnotations(annotations map[string]string) ([]Toleration, error) {
  427. var tolerations []Toleration
  428. if len(annotations) > 0 && annotations[TolerationsAnnotationKey] != "" {
  429. err := json.Unmarshal([]byte(annotations[TolerationsAnnotationKey]), &tolerations)
  430. if err != nil {
  431. return tolerations, err
  432. }
  433. }
  434. return tolerations, nil
  435. }
  436. // AddOrUpdateTolerationInPod tries to add a toleration to the pod's toleration list.
  437. // Returns true if something was updated, false otherwise.
  438. func AddOrUpdateTolerationInPod(pod *Pod, toleration *Toleration) (bool, error) {
  439. podTolerations := pod.Spec.Tolerations
  440. var newTolerations []Toleration
  441. updated := false
  442. for i := range podTolerations {
  443. if toleration.MatchToleration(&podTolerations[i]) {
  444. if Semantic.DeepEqual(toleration, podTolerations[i]) {
  445. return false, nil
  446. }
  447. newTolerations = append(newTolerations, *toleration)
  448. updated = true
  449. continue
  450. }
  451. newTolerations = append(newTolerations, podTolerations[i])
  452. }
  453. if !updated {
  454. newTolerations = append(newTolerations, *toleration)
  455. }
  456. pod.Spec.Tolerations = newTolerations
  457. return true, nil
  458. }
  459. // MatchToleration checks if the toleration matches tolerationToMatch. Tolerations are unique by <key,effect,operator,value>,
  460. // if the two tolerations have same <key,effect,operator,value> combination, regard as they match.
  461. // TODO: uniqueness check for tolerations in api validations.
  462. func (t *Toleration) MatchToleration(tolerationToMatch *Toleration) bool {
  463. return t.Key == tolerationToMatch.Key &&
  464. t.Effect == tolerationToMatch.Effect &&
  465. t.Operator == tolerationToMatch.Operator &&
  466. t.Value == tolerationToMatch.Value
  467. }
  468. // TolerationToleratesTaint checks if the toleration tolerates the taint.
  469. func TolerationToleratesTaint(toleration *Toleration, taint *Taint) bool {
  470. if len(toleration.Effect) != 0 && toleration.Effect != taint.Effect {
  471. return false
  472. }
  473. if toleration.Key != taint.Key {
  474. return false
  475. }
  476. // TODO: Use proper defaulting when Toleration becomes a field of PodSpec
  477. if (len(toleration.Operator) == 0 || toleration.Operator == TolerationOpEqual) && toleration.Value == taint.Value {
  478. return true
  479. }
  480. if toleration.Operator == TolerationOpExists {
  481. return true
  482. }
  483. return false
  484. }
  485. // TaintToleratedByTolerations checks if taint is tolerated by any of the tolerations.
  486. func TaintToleratedByTolerations(taint *Taint, tolerations []Toleration) bool {
  487. tolerated := false
  488. for i := range tolerations {
  489. if TolerationToleratesTaint(&tolerations[i], taint) {
  490. tolerated = true
  491. break
  492. }
  493. }
  494. return tolerated
  495. }
  496. // MatchTaint checks if the taint matches taintToMatch. Taints are unique by key:effect,
  497. // if the two taints have same key:effect, regard as they match.
  498. func (t *Taint) MatchTaint(taintToMatch Taint) bool {
  499. return t.Key == taintToMatch.Key && t.Effect == taintToMatch.Effect
  500. }
  501. // taint.ToString() converts taint struct to string in format key=value:effect or key:effect.
  502. func (t *Taint) ToString() string {
  503. if len(t.Value) == 0 {
  504. return fmt.Sprintf("%v:%v", t.Key, t.Effect)
  505. }
  506. return fmt.Sprintf("%v=%v:%v", t.Key, t.Value, t.Effect)
  507. }
  508. // GetTaintsFromNodeAnnotations gets the json serialized taints data from Pod.Annotations
  509. // and converts it to the []Taint type in api.
  510. func GetTaintsFromNodeAnnotations(annotations map[string]string) ([]Taint, error) {
  511. var taints []Taint
  512. if len(annotations) > 0 && annotations[TaintsAnnotationKey] != "" {
  513. err := json.Unmarshal([]byte(annotations[TaintsAnnotationKey]), &taints)
  514. if err != nil {
  515. return []Taint{}, err
  516. }
  517. }
  518. return taints, nil
  519. }
  520. // SysctlsFromPodAnnotations parses the sysctl annotations into a slice of safe Sysctls
  521. // and a slice of unsafe Sysctls. This is only a convenience wrapper around
  522. // SysctlsFromPodAnnotation.
  523. func SysctlsFromPodAnnotations(a map[string]string) ([]Sysctl, []Sysctl, error) {
  524. safe, err := SysctlsFromPodAnnotation(a[SysctlsPodAnnotationKey])
  525. if err != nil {
  526. return nil, nil, err
  527. }
  528. unsafe, err := SysctlsFromPodAnnotation(a[UnsafeSysctlsPodAnnotationKey])
  529. if err != nil {
  530. return nil, nil, err
  531. }
  532. return safe, unsafe, nil
  533. }
  534. // SysctlsFromPodAnnotation parses an annotation value into a slice of Sysctls.
  535. func SysctlsFromPodAnnotation(annotation string) ([]Sysctl, error) {
  536. if len(annotation) == 0 {
  537. return nil, nil
  538. }
  539. kvs := strings.Split(annotation, ",")
  540. sysctls := make([]Sysctl, len(kvs))
  541. for i, kv := range kvs {
  542. cs := strings.Split(kv, "=")
  543. if len(cs) != 2 || len(cs[0]) == 0 {
  544. return nil, fmt.Errorf("sysctl %q not of the format sysctl_name=value", kv)
  545. }
  546. sysctls[i].Name = cs[0]
  547. sysctls[i].Value = cs[1]
  548. }
  549. return sysctls, nil
  550. }
  551. // PodAnnotationsFromSysctls creates an annotation value for a slice of Sysctls.
  552. func PodAnnotationsFromSysctls(sysctls []Sysctl) string {
  553. if len(sysctls) == 0 {
  554. return ""
  555. }
  556. kvs := make([]string, len(sysctls))
  557. for i := range sysctls {
  558. kvs[i] = fmt.Sprintf("%s=%s", sysctls[i].Name, sysctls[i].Value)
  559. }
  560. return strings.Join(kvs, ",")
  561. }
  562. // GetAffinityFromPodAnnotations gets the json serialized affinity data from Pod.Annotations
  563. // and converts it to the Affinity type in api.
  564. // TODO: remove when alpha support for affinity is removed
  565. func GetAffinityFromPodAnnotations(annotations map[string]string) (*Affinity, error) {
  566. if len(annotations) > 0 && annotations[AffinityAnnotationKey] != "" {
  567. var affinity Affinity
  568. err := json.Unmarshal([]byte(annotations[AffinityAnnotationKey]), &affinity)
  569. if err != nil {
  570. return nil, err
  571. }
  572. return &affinity, nil
  573. }
  574. return nil, nil
  575. }
  576. // GetPersistentVolumeClass returns StorageClassName.
  577. func GetPersistentVolumeClass(volume *PersistentVolume) string {
  578. // Use beta annotation first
  579. if class, found := volume.Annotations[BetaStorageClassAnnotation]; found {
  580. return class
  581. }
  582. return volume.Spec.StorageClassName
  583. }
  584. // GetPersistentVolumeClaimClass returns StorageClassName. If no storage class was
  585. // requested, it returns "".
  586. func GetPersistentVolumeClaimClass(claim *PersistentVolumeClaim) string {
  587. // Use beta annotation first
  588. if class, found := claim.Annotations[BetaStorageClassAnnotation]; found {
  589. return class
  590. }
  591. if claim.Spec.StorageClassName != nil {
  592. return *claim.Spec.StorageClassName
  593. }
  594. return ""
  595. }
  596. // PersistentVolumeClaimHasClass returns true if given claim has set StorageClassName field.
  597. func PersistentVolumeClaimHasClass(claim *PersistentVolumeClaim) bool {
  598. // Use beta annotation first
  599. if _, found := claim.Annotations[BetaStorageClassAnnotation]; found {
  600. return true
  601. }
  602. if claim.Spec.StorageClassName != nil {
  603. return true
  604. }
  605. return false
  606. }