1
16
17 package helper
18
19 import (
20 "encoding/json"
21 "fmt"
22 "strconv"
23 "strings"
24
25 "k8s.io/apimachinery/pkg/api/resource"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/conversion"
28 "k8s.io/apimachinery/pkg/fields"
29 "k8s.io/apimachinery/pkg/labels"
30 "k8s.io/apimachinery/pkg/util/sets"
31 "k8s.io/apimachinery/pkg/util/validation"
32 "k8s.io/kubernetes/pkg/apis/core"
33 )
34
35
36
37 func IsHugePageResourceName(name core.ResourceName) bool {
38 return strings.HasPrefix(string(name), core.ResourceHugePagesPrefix)
39 }
40
41
42
43 func IsHugePageResourceValueDivisible(name core.ResourceName, quantity resource.Quantity) bool {
44 pageSize, err := HugePageSizeFromResourceName(name)
45 if err != nil {
46 return false
47 }
48
49 if pageSize.Sign() <= 0 || pageSize.MilliValue()%int64(1000) != int64(0) {
50 return false
51 }
52
53 return quantity.Value()%pageSize.Value() == 0
54 }
55
56
57
58 func IsQuotaHugePageResourceName(name core.ResourceName) bool {
59 return strings.HasPrefix(string(name), core.ResourceHugePagesPrefix) || strings.HasPrefix(string(name), core.ResourceRequestsHugePagesPrefix)
60 }
61
62
63
64
65 func HugePageResourceName(pageSize resource.Quantity) core.ResourceName {
66 return core.ResourceName(fmt.Sprintf("%s%s", core.ResourceHugePagesPrefix, pageSize.String()))
67 }
68
69
70
71
72 func HugePageSizeFromResourceName(name core.ResourceName) (resource.Quantity, error) {
73 if !IsHugePageResourceName(name) {
74 return resource.Quantity{}, fmt.Errorf("resource name: %s is an invalid hugepage name", name)
75 }
76 pageSize := strings.TrimPrefix(string(name), core.ResourceHugePagesPrefix)
77 return resource.ParseQuantity(pageSize)
78 }
79
80
81
82 func NonConvertibleFields(annotations map[string]string) map[string]string {
83 nonConvertibleKeys := map[string]string{}
84 for key, value := range annotations {
85 if strings.HasPrefix(key, core.NonConvertibleAnnotationPrefix) {
86 nonConvertibleKeys[key] = value
87 }
88 }
89 return nonConvertibleKeys
90 }
91
92
93
94 var Semantic = conversion.EqualitiesOrDie(
95 func(a, b resource.Quantity) bool {
96
97
98
99
100 return a.Cmp(b) == 0
101 },
102 func(a, b metav1.MicroTime) bool {
103 return a.UTC() == b.UTC()
104 },
105 func(a, b metav1.Time) bool {
106 return a.UTC() == b.UTC()
107 },
108 func(a, b labels.Selector) bool {
109 return a.String() == b.String()
110 },
111 func(a, b fields.Selector) bool {
112 return a.String() == b.String()
113 },
114 )
115
116 var standardResourceQuotaScopes = sets.New(
117 core.ResourceQuotaScopeTerminating,
118 core.ResourceQuotaScopeNotTerminating,
119 core.ResourceQuotaScopeBestEffort,
120 core.ResourceQuotaScopeNotBestEffort,
121 core.ResourceQuotaScopePriorityClass,
122 )
123
124
125 func IsStandardResourceQuotaScope(scope core.ResourceQuotaScope) bool {
126 return standardResourceQuotaScopes.Has(scope) || scope == core.ResourceQuotaScopeCrossNamespacePodAffinity
127 }
128
129 var podObjectCountQuotaResources = sets.New(
130 core.ResourcePods,
131 )
132
133 var podComputeQuotaResources = sets.New(
134 core.ResourceCPU,
135 core.ResourceMemory,
136 core.ResourceLimitsCPU,
137 core.ResourceLimitsMemory,
138 core.ResourceRequestsCPU,
139 core.ResourceRequestsMemory,
140 )
141
142
143 func IsResourceQuotaScopeValidForResource(scope core.ResourceQuotaScope, resource core.ResourceName) bool {
144 switch scope {
145 case core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating, core.ResourceQuotaScopeNotBestEffort,
146 core.ResourceQuotaScopePriorityClass, core.ResourceQuotaScopeCrossNamespacePodAffinity:
147 return podObjectCountQuotaResources.Has(resource) || podComputeQuotaResources.Has(resource)
148 case core.ResourceQuotaScopeBestEffort:
149 return podObjectCountQuotaResources.Has(resource)
150 default:
151 return true
152 }
153 }
154
155 var standardContainerResources = sets.New(
156 core.ResourceCPU,
157 core.ResourceMemory,
158 core.ResourceEphemeralStorage,
159 )
160
161
162
163 func IsStandardContainerResourceName(name core.ResourceName) bool {
164 return standardContainerResources.Has(name) || IsHugePageResourceName(name)
165 }
166
167
168
169
170
171
172 func IsExtendedResourceName(name core.ResourceName) bool {
173 if IsNativeResource(name) || strings.HasPrefix(string(name), core.DefaultResourceRequestsPrefix) {
174 return false
175 }
176
177 nameForQuota := fmt.Sprintf("%s%s", core.DefaultResourceRequestsPrefix, string(name))
178 if errs := validation.IsQualifiedName(nameForQuota); len(errs) != 0 {
179 return false
180 }
181 return true
182 }
183
184
185
186
187 func IsNativeResource(name core.ResourceName) bool {
188 return !strings.Contains(string(name), "/") ||
189 strings.Contains(string(name), core.ResourceDefaultNamespacePrefix)
190 }
191
192
193
194 func IsOvercommitAllowed(name core.ResourceName) bool {
195 return IsNativeResource(name) &&
196 !IsHugePageResourceName(name)
197 }
198
199 var standardLimitRangeTypes = sets.New(
200 core.LimitTypePod,
201 core.LimitTypeContainer,
202 core.LimitTypePersistentVolumeClaim,
203 )
204
205
206 func IsStandardLimitRangeType(value core.LimitType) bool {
207 return standardLimitRangeTypes.Has(value)
208 }
209
210 var standardQuotaResources = sets.New(
211 core.ResourceCPU,
212 core.ResourceMemory,
213 core.ResourceEphemeralStorage,
214 core.ResourceRequestsCPU,
215 core.ResourceRequestsMemory,
216 core.ResourceRequestsStorage,
217 core.ResourceRequestsEphemeralStorage,
218 core.ResourceLimitsCPU,
219 core.ResourceLimitsMemory,
220 core.ResourceLimitsEphemeralStorage,
221 core.ResourcePods,
222 core.ResourceQuotas,
223 core.ResourceServices,
224 core.ResourceReplicationControllers,
225 core.ResourceSecrets,
226 core.ResourcePersistentVolumeClaims,
227 core.ResourceConfigMaps,
228 core.ResourceServicesNodePorts,
229 core.ResourceServicesLoadBalancers,
230 )
231
232
233
234 func IsStandardQuotaResourceName(name core.ResourceName) bool {
235 return standardQuotaResources.Has(name) || IsQuotaHugePageResourceName(name)
236 }
237
238 var standardResources = sets.New(
239 core.ResourceCPU,
240 core.ResourceMemory,
241 core.ResourceEphemeralStorage,
242 core.ResourceRequestsCPU,
243 core.ResourceRequestsMemory,
244 core.ResourceRequestsEphemeralStorage,
245 core.ResourceLimitsCPU,
246 core.ResourceLimitsMemory,
247 core.ResourceLimitsEphemeralStorage,
248 core.ResourcePods,
249 core.ResourceQuotas,
250 core.ResourceServices,
251 core.ResourceReplicationControllers,
252 core.ResourceSecrets,
253 core.ResourceConfigMaps,
254 core.ResourcePersistentVolumeClaims,
255 core.ResourceStorage,
256 core.ResourceRequestsStorage,
257 core.ResourceServicesNodePorts,
258 core.ResourceServicesLoadBalancers,
259 )
260
261
262 func IsStandardResourceName(name core.ResourceName) bool {
263 return standardResources.Has(name) || IsQuotaHugePageResourceName(name)
264 }
265
266 var integerResources = sets.New(
267 core.ResourcePods,
268 core.ResourceQuotas,
269 core.ResourceServices,
270 core.ResourceReplicationControllers,
271 core.ResourceSecrets,
272 core.ResourceConfigMaps,
273 core.ResourcePersistentVolumeClaims,
274 core.ResourceServicesNodePorts,
275 core.ResourceServicesLoadBalancers,
276 )
277
278
279 func IsIntegerResourceName(name core.ResourceName) bool {
280 return integerResources.Has(name) || IsExtendedResourceName(name)
281 }
282
283
284
285 func IsServiceIPSet(service *core.Service) bool {
286
287
288 return len(service.Spec.ClusterIP) > 0 &&
289 service.Spec.ClusterIP != core.ClusterIPNone
290 }
291
292 var standardFinalizers = sets.New(
293 string(core.FinalizerKubernetes),
294 metav1.FinalizerOrphanDependents,
295 metav1.FinalizerDeleteDependents,
296 )
297
298
299 func IsStandardFinalizerName(str string) bool {
300 return standardFinalizers.Has(str)
301 }
302
303
304
305 func GetAccessModesAsString(modes []core.PersistentVolumeAccessMode) string {
306 modes = removeDuplicateAccessModes(modes)
307 modesStr := []string{}
308 if ContainsAccessMode(modes, core.ReadWriteOnce) {
309 modesStr = append(modesStr, "RWO")
310 }
311 if ContainsAccessMode(modes, core.ReadOnlyMany) {
312 modesStr = append(modesStr, "ROX")
313 }
314 if ContainsAccessMode(modes, core.ReadWriteMany) {
315 modesStr = append(modesStr, "RWX")
316 }
317 if ContainsAccessMode(modes, core.ReadWriteOncePod) {
318 modesStr = append(modesStr, "RWOP")
319 }
320 return strings.Join(modesStr, ",")
321 }
322
323
324 func GetAccessModesFromString(modes string) []core.PersistentVolumeAccessMode {
325 strmodes := strings.Split(modes, ",")
326 accessModes := []core.PersistentVolumeAccessMode{}
327 for _, s := range strmodes {
328 s = strings.Trim(s, " ")
329 switch {
330 case s == "RWO":
331 accessModes = append(accessModes, core.ReadWriteOnce)
332 case s == "ROX":
333 accessModes = append(accessModes, core.ReadOnlyMany)
334 case s == "RWX":
335 accessModes = append(accessModes, core.ReadWriteMany)
336 case s == "RWOP":
337 accessModes = append(accessModes, core.ReadWriteOncePod)
338 }
339 }
340 return accessModes
341 }
342
343
344 func removeDuplicateAccessModes(modes []core.PersistentVolumeAccessMode) []core.PersistentVolumeAccessMode {
345 accessModes := []core.PersistentVolumeAccessMode{}
346 for _, m := range modes {
347 if !ContainsAccessMode(accessModes, m) {
348 accessModes = append(accessModes, m)
349 }
350 }
351 return accessModes
352 }
353
354 func ContainsAccessMode(modes []core.PersistentVolumeAccessMode, mode core.PersistentVolumeAccessMode) bool {
355 for _, m := range modes {
356 if m == mode {
357 return true
358 }
359 }
360 return false
361 }
362
363 func ClaimContainsAllocatedResources(pvc *core.PersistentVolumeClaim) bool {
364 if pvc == nil {
365 return false
366 }
367
368 if pvc.Status.AllocatedResources != nil {
369 return true
370 }
371 return false
372 }
373
374 func ClaimContainsAllocatedResourceStatus(pvc *core.PersistentVolumeClaim) bool {
375 if pvc == nil {
376 return false
377 }
378
379 if pvc.Status.AllocatedResourceStatuses != nil {
380 return true
381 }
382 return false
383 }
384
385
386
387 func GetTolerationsFromPodAnnotations(annotations map[string]string) ([]core.Toleration, error) {
388 var tolerations []core.Toleration
389 if len(annotations) > 0 && annotations[core.TolerationsAnnotationKey] != "" {
390 err := json.Unmarshal([]byte(annotations[core.TolerationsAnnotationKey]), &tolerations)
391 if err != nil {
392 return tolerations, err
393 }
394 }
395 return tolerations, nil
396 }
397
398
399
400 func AddOrUpdateTolerationInPod(pod *core.Pod, toleration *core.Toleration) bool {
401 podTolerations := pod.Spec.Tolerations
402
403 var newTolerations []core.Toleration
404 updated := false
405 for i := range podTolerations {
406 if toleration.MatchToleration(&podTolerations[i]) {
407 if Semantic.DeepEqual(toleration, podTolerations[i]) {
408 return false
409 }
410 newTolerations = append(newTolerations, *toleration)
411 updated = true
412 continue
413 }
414
415 newTolerations = append(newTolerations, podTolerations[i])
416 }
417
418 if !updated {
419 newTolerations = append(newTolerations, *toleration)
420 }
421
422 pod.Spec.Tolerations = newTolerations
423 return true
424 }
425
426
427
428 func GetTaintsFromNodeAnnotations(annotations map[string]string) ([]core.Taint, error) {
429 var taints []core.Taint
430 if len(annotations) > 0 && annotations[core.TaintsAnnotationKey] != "" {
431 err := json.Unmarshal([]byte(annotations[core.TaintsAnnotationKey]), &taints)
432 if err != nil {
433 return []core.Taint{}, err
434 }
435 }
436 return taints, nil
437 }
438
439
440 func GetPersistentVolumeClass(volume *core.PersistentVolume) string {
441
442 if class, found := volume.Annotations[core.BetaStorageClassAnnotation]; found {
443 return class
444 }
445
446 return volume.Spec.StorageClassName
447 }
448
449
450
451 func GetPersistentVolumeClaimClass(claim *core.PersistentVolumeClaim) string {
452
453 if class, found := claim.Annotations[core.BetaStorageClassAnnotation]; found {
454 return class
455 }
456
457 if claim.Spec.StorageClassName != nil {
458 return *claim.Spec.StorageClassName
459 }
460
461 return ""
462 }
463
464
465 func PersistentVolumeClaimHasClass(claim *core.PersistentVolumeClaim) bool {
466
467 if _, found := claim.Annotations[core.BetaStorageClassAnnotation]; found {
468 return true
469 }
470
471 if claim.Spec.StorageClassName != nil {
472 return true
473 }
474
475 return false
476 }
477
478
479
480 func GetDeletionCostFromPodAnnotations(annotations map[string]string) (int32, error) {
481 if value, exist := annotations[core.PodDeletionCost]; exist {
482
483 if !validFirstDigit(value) {
484 return 0, fmt.Errorf("invalid value %q", value)
485 }
486
487 i, err := strconv.ParseInt(value, 10, 32)
488 if err != nil {
489
490 return 0, err
491 }
492 return int32(i), nil
493 }
494 return 0, nil
495 }
496
497 func validFirstDigit(str string) bool {
498 if len(str) == 0 {
499 return false
500 }
501 return str[0] == '-' || (str[0] == '0' && str == "0") || (str[0] >= '1' && str[0] <= '9')
502 }
503
View as plain text