helpers.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  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. metav1.FinalizerDeleteDependents,
  218. )
  219. // HasAnnotation returns a bool if passed in annotation exists
  220. func HasAnnotation(obj ObjectMeta, ann string) bool {
  221. _, found := obj.Annotations[ann]
  222. return found
  223. }
  224. // SetMetaDataAnnotation sets the annotation and value
  225. func SetMetaDataAnnotation(obj *ObjectMeta, ann string, value string) {
  226. if obj.Annotations == nil {
  227. obj.Annotations = make(map[string]string)
  228. }
  229. obj.Annotations[ann] = value
  230. }
  231. func IsStandardFinalizerName(str string) bool {
  232. return standardFinalizers.Has(str)
  233. }
  234. // AddToNodeAddresses appends the NodeAddresses to the passed-by-pointer slice,
  235. // only if they do not already exist
  236. func AddToNodeAddresses(addresses *[]NodeAddress, addAddresses ...NodeAddress) {
  237. for _, add := range addAddresses {
  238. exists := false
  239. for _, existing := range *addresses {
  240. if existing.Address == add.Address && existing.Type == add.Type {
  241. exists = true
  242. break
  243. }
  244. }
  245. if !exists {
  246. *addresses = append(*addresses, add)
  247. }
  248. }
  249. }
  250. func HashObject(obj runtime.Object, codec runtime.Codec) (string, error) {
  251. data, err := runtime.Encode(codec, obj)
  252. if err != nil {
  253. return "", err
  254. }
  255. return fmt.Sprintf("%x", md5.Sum(data)), nil
  256. }
  257. // TODO: make method on LoadBalancerStatus?
  258. func LoadBalancerStatusEqual(l, r *LoadBalancerStatus) bool {
  259. return ingressSliceEqual(l.Ingress, r.Ingress)
  260. }
  261. func ingressSliceEqual(lhs, rhs []LoadBalancerIngress) bool {
  262. if len(lhs) != len(rhs) {
  263. return false
  264. }
  265. for i := range lhs {
  266. if !ingressEqual(&lhs[i], &rhs[i]) {
  267. return false
  268. }
  269. }
  270. return true
  271. }
  272. func ingressEqual(lhs, rhs *LoadBalancerIngress) bool {
  273. if lhs.IP != rhs.IP {
  274. return false
  275. }
  276. if lhs.Hostname != rhs.Hostname {
  277. return false
  278. }
  279. return true
  280. }
  281. // TODO: make method on LoadBalancerStatus?
  282. func LoadBalancerStatusDeepCopy(lb *LoadBalancerStatus) *LoadBalancerStatus {
  283. c := &LoadBalancerStatus{}
  284. c.Ingress = make([]LoadBalancerIngress, len(lb.Ingress))
  285. for i := range lb.Ingress {
  286. c.Ingress[i] = lb.Ingress[i]
  287. }
  288. return c
  289. }
  290. // GetAccessModesAsString returns a string representation of an array of access modes.
  291. // modes, when present, are always in the same order: RWO,ROX,RWX.
  292. func GetAccessModesAsString(modes []PersistentVolumeAccessMode) string {
  293. modes = removeDuplicateAccessModes(modes)
  294. modesStr := []string{}
  295. if containsAccessMode(modes, ReadWriteOnce) {
  296. modesStr = append(modesStr, "RWO")
  297. }
  298. if containsAccessMode(modes, ReadOnlyMany) {
  299. modesStr = append(modesStr, "ROX")
  300. }
  301. if containsAccessMode(modes, ReadWriteMany) {
  302. modesStr = append(modesStr, "RWX")
  303. }
  304. return strings.Join(modesStr, ",")
  305. }
  306. // GetAccessModesAsString returns an array of AccessModes from a string created by GetAccessModesAsString
  307. func GetAccessModesFromString(modes string) []PersistentVolumeAccessMode {
  308. strmodes := strings.Split(modes, ",")
  309. accessModes := []PersistentVolumeAccessMode{}
  310. for _, s := range strmodes {
  311. s = strings.Trim(s, " ")
  312. switch {
  313. case s == "RWO":
  314. accessModes = append(accessModes, ReadWriteOnce)
  315. case s == "ROX":
  316. accessModes = append(accessModes, ReadOnlyMany)
  317. case s == "RWX":
  318. accessModes = append(accessModes, ReadWriteMany)
  319. }
  320. }
  321. return accessModes
  322. }
  323. // removeDuplicateAccessModes returns an array of access modes without any duplicates
  324. func removeDuplicateAccessModes(modes []PersistentVolumeAccessMode) []PersistentVolumeAccessMode {
  325. accessModes := []PersistentVolumeAccessMode{}
  326. for _, m := range modes {
  327. if !containsAccessMode(accessModes, m) {
  328. accessModes = append(accessModes, m)
  329. }
  330. }
  331. return accessModes
  332. }
  333. func containsAccessMode(modes []PersistentVolumeAccessMode, mode PersistentVolumeAccessMode) bool {
  334. for _, m := range modes {
  335. if m == mode {
  336. return true
  337. }
  338. }
  339. return false
  340. }
  341. // ParseRFC3339 parses an RFC3339 date in either RFC3339Nano or RFC3339 format.
  342. func ParseRFC3339(s string, nowFn func() metav1.Time) (metav1.Time, error) {
  343. if t, timeErr := time.Parse(time.RFC3339Nano, s); timeErr == nil {
  344. return metav1.Time{Time: t}, nil
  345. }
  346. t, err := time.Parse(time.RFC3339, s)
  347. if err != nil {
  348. return metav1.Time{}, err
  349. }
  350. return metav1.Time{Time: t}, nil
  351. }
  352. // NodeSelectorRequirementsAsSelector converts the []NodeSelectorRequirement api type into a struct that implements
  353. // labels.Selector.
  354. func NodeSelectorRequirementsAsSelector(nsm []NodeSelectorRequirement) (labels.Selector, error) {
  355. if len(nsm) == 0 {
  356. return labels.Nothing(), nil
  357. }
  358. selector := labels.NewSelector()
  359. for _, expr := range nsm {
  360. var op selection.Operator
  361. switch expr.Operator {
  362. case NodeSelectorOpIn:
  363. op = selection.In
  364. case NodeSelectorOpNotIn:
  365. op = selection.NotIn
  366. case NodeSelectorOpExists:
  367. op = selection.Exists
  368. case NodeSelectorOpDoesNotExist:
  369. op = selection.DoesNotExist
  370. case NodeSelectorOpGt:
  371. op = selection.GreaterThan
  372. case NodeSelectorOpLt:
  373. op = selection.LessThan
  374. default:
  375. return nil, fmt.Errorf("%q is not a valid node selector operator", expr.Operator)
  376. }
  377. r, err := labels.NewRequirement(expr.Key, op, expr.Values)
  378. if err != nil {
  379. return nil, err
  380. }
  381. selector = selector.Add(*r)
  382. }
  383. return selector, nil
  384. }
  385. const (
  386. // TolerationsAnnotationKey represents the key of tolerations data (json serialized)
  387. // in the Annotations of a Pod.
  388. TolerationsAnnotationKey string = "scheduler.alpha.kubernetes.io/tolerations"
  389. // TaintsAnnotationKey represents the key of taints data (json serialized)
  390. // in the Annotations of a Node.
  391. TaintsAnnotationKey string = "scheduler.alpha.kubernetes.io/taints"
  392. // SeccompPodAnnotationKey represents the key of a seccomp profile applied
  393. // to all containers of a pod.
  394. SeccompPodAnnotationKey string = "seccomp.security.alpha.kubernetes.io/pod"
  395. // SeccompContainerAnnotationKeyPrefix represents the key of a seccomp profile applied
  396. // to one container of a pod.
  397. SeccompContainerAnnotationKeyPrefix string = "container.seccomp.security.alpha.kubernetes.io/"
  398. // CreatedByAnnotation represents the key used to store the spec(json)
  399. // used to create the resource.
  400. CreatedByAnnotation = "kubernetes.io/created-by"
  401. // PreferAvoidPodsAnnotationKey represents the key of preferAvoidPods data (json serialized)
  402. // in the Annotations of a Node.
  403. PreferAvoidPodsAnnotationKey string = "scheduler.alpha.kubernetes.io/preferAvoidPods"
  404. // SysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
  405. // container of a pod. The annotation value is a comma separated list of sysctl_name=value
  406. // key-value pairs. Only a limited set of whitelisted and isolated sysctls is supported by
  407. // the kubelet. Pods with other sysctls will fail to launch.
  408. SysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/sysctls"
  409. // UnsafeSysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
  410. // container of a pod. The annotation value is a comma separated list of sysctl_name=value
  411. // key-value pairs. Unsafe sysctls must be explicitly enabled for a kubelet. They are properly
  412. // namespaced to a pod or a container, but their isolation is usually unclear or weak. Their use
  413. // is at-your-own-risk. Pods that attempt to set an unsafe sysctl that is not enabled for a kubelet
  414. // will fail to launch.
  415. UnsafeSysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/unsafe-sysctls"
  416. // ObjectTTLAnnotations represents a suggestion for kubelet for how long it can cache
  417. // an object (e.g. secret, config map) before fetching it again from apiserver.
  418. // This annotation can be attached to node.
  419. ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl"
  420. // AffinityAnnotationKey represents the key of affinity data (json serialized)
  421. // in the Annotations of a Pod.
  422. // TODO: remove when alpha support for affinity is removed
  423. AffinityAnnotationKey string = "scheduler.alpha.kubernetes.io/affinity"
  424. )
  425. // GetTolerationsFromPodAnnotations gets the json serialized tolerations data from Pod.Annotations
  426. // and converts it to the []Toleration type in api.
  427. func GetTolerationsFromPodAnnotations(annotations map[string]string) ([]Toleration, error) {
  428. var tolerations []Toleration
  429. if len(annotations) > 0 && annotations[TolerationsAnnotationKey] != "" {
  430. err := json.Unmarshal([]byte(annotations[TolerationsAnnotationKey]), &tolerations)
  431. if err != nil {
  432. return tolerations, err
  433. }
  434. }
  435. return tolerations, nil
  436. }
  437. // AddOrUpdateTolerationInPod tries to add a toleration to the pod's toleration list.
  438. // Returns true if something was updated, false otherwise.
  439. func AddOrUpdateTolerationInPod(pod *Pod, toleration *Toleration) (bool, error) {
  440. podTolerations := pod.Spec.Tolerations
  441. var newTolerations []Toleration
  442. updated := false
  443. for i := range podTolerations {
  444. if toleration.MatchToleration(&podTolerations[i]) {
  445. if Semantic.DeepEqual(toleration, podTolerations[i]) {
  446. return false, nil
  447. }
  448. newTolerations = append(newTolerations, *toleration)
  449. updated = true
  450. continue
  451. }
  452. newTolerations = append(newTolerations, podTolerations[i])
  453. }
  454. if !updated {
  455. newTolerations = append(newTolerations, *toleration)
  456. }
  457. pod.Spec.Tolerations = newTolerations
  458. return true, nil
  459. }
  460. // MatchToleration checks if the toleration matches tolerationToMatch. Tolerations are unique by <key,effect,operator,value>,
  461. // if the two tolerations have same <key,effect,operator,value> combination, regard as they match.
  462. // TODO: uniqueness check for tolerations in api validations.
  463. func (t *Toleration) MatchToleration(tolerationToMatch *Toleration) bool {
  464. return t.Key == tolerationToMatch.Key &&
  465. t.Effect == tolerationToMatch.Effect &&
  466. t.Operator == tolerationToMatch.Operator &&
  467. t.Value == tolerationToMatch.Value
  468. }
  469. // TolerationToleratesTaint checks if the toleration tolerates the taint.
  470. func TolerationToleratesTaint(toleration *Toleration, taint *Taint) bool {
  471. if len(toleration.Effect) != 0 && toleration.Effect != taint.Effect {
  472. return false
  473. }
  474. if toleration.Key != taint.Key {
  475. return false
  476. }
  477. // TODO: Use proper defaulting when Toleration becomes a field of PodSpec
  478. if (len(toleration.Operator) == 0 || toleration.Operator == TolerationOpEqual) && toleration.Value == taint.Value {
  479. return true
  480. }
  481. if toleration.Operator == TolerationOpExists {
  482. return true
  483. }
  484. return false
  485. }
  486. // TaintToleratedByTolerations checks if taint is tolerated by any of the tolerations.
  487. func TaintToleratedByTolerations(taint *Taint, tolerations []Toleration) bool {
  488. tolerated := false
  489. for i := range tolerations {
  490. if TolerationToleratesTaint(&tolerations[i], taint) {
  491. tolerated = true
  492. break
  493. }
  494. }
  495. return tolerated
  496. }
  497. // MatchTaint checks if the taint matches taintToMatch. Taints are unique by key:effect,
  498. // if the two taints have same key:effect, regard as they match.
  499. func (t *Taint) MatchTaint(taintToMatch Taint) bool {
  500. return t.Key == taintToMatch.Key && t.Effect == taintToMatch.Effect
  501. }
  502. // taint.ToString() converts taint struct to string in format key=value:effect or key:effect.
  503. func (t *Taint) ToString() string {
  504. if len(t.Value) == 0 {
  505. return fmt.Sprintf("%v:%v", t.Key, t.Effect)
  506. }
  507. return fmt.Sprintf("%v=%v:%v", t.Key, t.Value, t.Effect)
  508. }
  509. // GetTaintsFromNodeAnnotations gets the json serialized taints data from Pod.Annotations
  510. // and converts it to the []Taint type in api.
  511. func GetTaintsFromNodeAnnotations(annotations map[string]string) ([]Taint, error) {
  512. var taints []Taint
  513. if len(annotations) > 0 && annotations[TaintsAnnotationKey] != "" {
  514. err := json.Unmarshal([]byte(annotations[TaintsAnnotationKey]), &taints)
  515. if err != nil {
  516. return []Taint{}, err
  517. }
  518. }
  519. return taints, nil
  520. }
  521. // SysctlsFromPodAnnotations parses the sysctl annotations into a slice of safe Sysctls
  522. // and a slice of unsafe Sysctls. This is only a convenience wrapper around
  523. // SysctlsFromPodAnnotation.
  524. func SysctlsFromPodAnnotations(a map[string]string) ([]Sysctl, []Sysctl, error) {
  525. safe, err := SysctlsFromPodAnnotation(a[SysctlsPodAnnotationKey])
  526. if err != nil {
  527. return nil, nil, err
  528. }
  529. unsafe, err := SysctlsFromPodAnnotation(a[UnsafeSysctlsPodAnnotationKey])
  530. if err != nil {
  531. return nil, nil, err
  532. }
  533. return safe, unsafe, nil
  534. }
  535. // SysctlsFromPodAnnotation parses an annotation value into a slice of Sysctls.
  536. func SysctlsFromPodAnnotation(annotation string) ([]Sysctl, error) {
  537. if len(annotation) == 0 {
  538. return nil, nil
  539. }
  540. kvs := strings.Split(annotation, ",")
  541. sysctls := make([]Sysctl, len(kvs))
  542. for i, kv := range kvs {
  543. cs := strings.Split(kv, "=")
  544. if len(cs) != 2 || len(cs[0]) == 0 {
  545. return nil, fmt.Errorf("sysctl %q not of the format sysctl_name=value", kv)
  546. }
  547. sysctls[i].Name = cs[0]
  548. sysctls[i].Value = cs[1]
  549. }
  550. return sysctls, nil
  551. }
  552. // PodAnnotationsFromSysctls creates an annotation value for a slice of Sysctls.
  553. func PodAnnotationsFromSysctls(sysctls []Sysctl) string {
  554. if len(sysctls) == 0 {
  555. return ""
  556. }
  557. kvs := make([]string, len(sysctls))
  558. for i := range sysctls {
  559. kvs[i] = fmt.Sprintf("%s=%s", sysctls[i].Name, sysctls[i].Value)
  560. }
  561. return strings.Join(kvs, ",")
  562. }
  563. // GetAffinityFromPodAnnotations gets the json serialized affinity data from Pod.Annotations
  564. // and converts it to the Affinity type in api.
  565. // TODO: remove when alpha support for affinity is removed
  566. func GetAffinityFromPodAnnotations(annotations map[string]string) (*Affinity, error) {
  567. if len(annotations) > 0 && annotations[AffinityAnnotationKey] != "" {
  568. var affinity Affinity
  569. err := json.Unmarshal([]byte(annotations[AffinityAnnotationKey]), &affinity)
  570. if err != nil {
  571. return nil, err
  572. }
  573. return &affinity, nil
  574. }
  575. return nil, nil
  576. }
  577. // GetPersistentVolumeClass returns StorageClassName.
  578. func GetPersistentVolumeClass(volume *PersistentVolume) string {
  579. // Use beta annotation first
  580. if class, found := volume.Annotations[BetaStorageClassAnnotation]; found {
  581. return class
  582. }
  583. return volume.Spec.StorageClassName
  584. }
  585. // GetPersistentVolumeClaimClass returns StorageClassName. If no storage class was
  586. // requested, it returns "".
  587. func GetPersistentVolumeClaimClass(claim *PersistentVolumeClaim) string {
  588. // Use beta annotation first
  589. if class, found := claim.Annotations[BetaStorageClassAnnotation]; found {
  590. return class
  591. }
  592. if claim.Spec.StorageClassName != nil {
  593. return *claim.Spec.StorageClassName
  594. }
  595. return ""
  596. }
  597. // PersistentVolumeClaimHasClass returns true if given claim has set StorageClassName field.
  598. func PersistentVolumeClaimHasClass(claim *PersistentVolumeClaim) bool {
  599. // Use beta annotation first
  600. if _, found := claim.Annotations[BetaStorageClassAnnotation]; found {
  601. return true
  602. }
  603. if claim.Spec.StorageClassName != nil {
  604. return true
  605. }
  606. return false
  607. }