1
16
17 package helper
18
19 import (
20 "fmt"
21 "strings"
22
23 v1 "k8s.io/api/core/v1"
24 "k8s.io/apimachinery/pkg/api/resource"
25 "k8s.io/apimachinery/pkg/labels"
26 "k8s.io/apimachinery/pkg/selection"
27 "k8s.io/apimachinery/pkg/util/validation"
28 "k8s.io/kubernetes/pkg/apis/core/helper"
29 )
30
31
32
33
34
35
36 func IsExtendedResourceName(name v1.ResourceName) bool {
37 if IsNativeResource(name) || strings.HasPrefix(string(name), v1.DefaultResourceRequestsPrefix) {
38 return false
39 }
40
41 nameForQuota := fmt.Sprintf("%s%s", v1.DefaultResourceRequestsPrefix, string(name))
42 if errs := validation.IsQualifiedName(nameForQuota); len(errs) != 0 {
43 return false
44 }
45 return true
46 }
47
48
49
50 func IsPrefixedNativeResource(name v1.ResourceName) bool {
51 return strings.Contains(string(name), v1.ResourceDefaultNamespacePrefix)
52 }
53
54
55
56
57 func IsNativeResource(name v1.ResourceName) bool {
58 return !strings.Contains(string(name), "/") ||
59 IsPrefixedNativeResource(name)
60 }
61
62
63
64 func IsHugePageResourceName(name v1.ResourceName) bool {
65 return strings.HasPrefix(string(name), v1.ResourceHugePagesPrefix)
66 }
67
68
69
70
71 func HugePageResourceName(pageSize resource.Quantity) v1.ResourceName {
72 return v1.ResourceName(fmt.Sprintf("%s%s", v1.ResourceHugePagesPrefix, pageSize.String()))
73 }
74
75
76
77
78 func HugePageSizeFromResourceName(name v1.ResourceName) (resource.Quantity, error) {
79 if !IsHugePageResourceName(name) {
80 return resource.Quantity{}, fmt.Errorf("resource name: %s is an invalid hugepage name", name)
81 }
82 pageSize := strings.TrimPrefix(string(name), v1.ResourceHugePagesPrefix)
83 return resource.ParseQuantity(pageSize)
84 }
85
86
87
88
89 func HugePageUnitSizeFromByteSize(size int64) (string, error) {
90
91 var hugePageSizeUnitList = []string{"B", "KB", "MB", "GB", "TB", "PB"}
92 idx := 0
93 len := len(hugePageSizeUnitList) - 1
94 for size%1024 == 0 && idx < len {
95 size /= 1024
96 idx++
97 }
98 if size > 1024 && idx < len {
99 return "", fmt.Errorf("size: %d%s must be guaranteed to divisible into the largest units", size, hugePageSizeUnitList[idx])
100 }
101 return fmt.Sprintf("%d%s", size, hugePageSizeUnitList[idx]), nil
102 }
103
104
105 func IsHugePageMedium(medium v1.StorageMedium) bool {
106 if medium == v1.StorageMediumHugePages {
107 return true
108 }
109 return strings.HasPrefix(string(medium), string(v1.StorageMediumHugePagesPrefix))
110 }
111
112
113
114 func HugePageSizeFromMedium(medium v1.StorageMedium) (resource.Quantity, error) {
115 if !IsHugePageMedium(medium) {
116 return resource.Quantity{}, fmt.Errorf("medium: %s is not a hugepage medium", medium)
117 }
118 if medium == v1.StorageMediumHugePages {
119 return resource.Quantity{}, fmt.Errorf("medium: %s doesn't have size information", medium)
120 }
121 pageSize := strings.TrimPrefix(string(medium), string(v1.StorageMediumHugePagesPrefix))
122 return resource.ParseQuantity(pageSize)
123 }
124
125
126
127 func IsOvercommitAllowed(name v1.ResourceName) bool {
128 return IsNativeResource(name) &&
129 !IsHugePageResourceName(name)
130 }
131
132
133 func IsAttachableVolumeResourceName(name v1.ResourceName) bool {
134 return strings.HasPrefix(string(name), v1.ResourceAttachableVolumesPrefix)
135 }
136
137
138
139 func IsServiceIPSet(service *v1.Service) bool {
140 return service.Spec.ClusterIP != v1.ClusterIPNone && service.Spec.ClusterIP != ""
141 }
142
143
144
145
146 func LoadBalancerStatusEqual(l, r *v1.LoadBalancerStatus) bool {
147 return ingressSliceEqual(l.Ingress, r.Ingress)
148 }
149
150 func ingressSliceEqual(lhs, rhs []v1.LoadBalancerIngress) bool {
151 if len(lhs) != len(rhs) {
152 return false
153 }
154 for i := range lhs {
155 if !ingressEqual(&lhs[i], &rhs[i]) {
156 return false
157 }
158 }
159 return true
160 }
161
162 func ingressEqual(lhs, rhs *v1.LoadBalancerIngress) bool {
163 if lhs.IP != rhs.IP {
164 return false
165 }
166 if lhs.Hostname != rhs.Hostname {
167 return false
168 }
169 return true
170 }
171
172
173
174 func GetAccessModesAsString(modes []v1.PersistentVolumeAccessMode) string {
175 modes = removeDuplicateAccessModes(modes)
176 modesStr := []string{}
177 if ContainsAccessMode(modes, v1.ReadWriteOnce) {
178 modesStr = append(modesStr, "RWO")
179 }
180 if ContainsAccessMode(modes, v1.ReadOnlyMany) {
181 modesStr = append(modesStr, "ROX")
182 }
183 if ContainsAccessMode(modes, v1.ReadWriteMany) {
184 modesStr = append(modesStr, "RWX")
185 }
186 if ContainsAccessMode(modes, v1.ReadWriteOncePod) {
187 modesStr = append(modesStr, "RWOP")
188 }
189 return strings.Join(modesStr, ",")
190 }
191
192
193 func GetAccessModesFromString(modes string) []v1.PersistentVolumeAccessMode {
194 strmodes := strings.Split(modes, ",")
195 accessModes := []v1.PersistentVolumeAccessMode{}
196 for _, s := range strmodes {
197 s = strings.Trim(s, " ")
198 switch {
199 case s == "RWO":
200 accessModes = append(accessModes, v1.ReadWriteOnce)
201 case s == "ROX":
202 accessModes = append(accessModes, v1.ReadOnlyMany)
203 case s == "RWX":
204 accessModes = append(accessModes, v1.ReadWriteMany)
205 case s == "RWOP":
206 accessModes = append(accessModes, v1.ReadWriteOncePod)
207 }
208 }
209 return accessModes
210 }
211
212
213 func removeDuplicateAccessModes(modes []v1.PersistentVolumeAccessMode) []v1.PersistentVolumeAccessMode {
214 accessModes := []v1.PersistentVolumeAccessMode{}
215 for _, m := range modes {
216 if !ContainsAccessMode(accessModes, m) {
217 accessModes = append(accessModes, m)
218 }
219 }
220 return accessModes
221 }
222
223 func ContainsAccessMode(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool {
224 for _, m := range modes {
225 if m == mode {
226 return true
227 }
228 }
229 return false
230 }
231
232
233 func NodeSelectorRequirementKeysExistInNodeSelectorTerms(reqs []v1.NodeSelectorRequirement, terms []v1.NodeSelectorTerm) bool {
234 for _, req := range reqs {
235 for _, term := range terms {
236 for _, r := range term.MatchExpressions {
237 if r.Key == req.Key {
238 return true
239 }
240 }
241 }
242 }
243 return false
244 }
245
246
247
248 func TopologySelectorRequirementsAsSelector(tsm []v1.TopologySelectorLabelRequirement) (labels.Selector, error) {
249 if len(tsm) == 0 {
250 return labels.Nothing(), nil
251 }
252
253 selector := labels.NewSelector()
254 for _, expr := range tsm {
255 r, err := labels.NewRequirement(expr.Key, selection.In, expr.Values)
256 if err != nil {
257 return nil, err
258 }
259 selector = selector.Add(*r)
260 }
261
262 return selector, nil
263 }
264
265
266
267 func MatchTopologySelectorTerms(topologySelectorTerms []v1.TopologySelectorTerm, lbls labels.Set) bool {
268 if len(topologySelectorTerms) == 0 {
269
270 return true
271 }
272
273 for _, req := range topologySelectorTerms {
274
275 if len(req.MatchLabelExpressions) == 0 {
276 continue
277 }
278
279 labelSelector, err := TopologySelectorRequirementsAsSelector(req.MatchLabelExpressions)
280 if err != nil || !labelSelector.Matches(lbls) {
281 continue
282 }
283
284 return true
285 }
286
287 return false
288 }
289
290
291
292 func AddOrUpdateTolerationInPodSpec(spec *v1.PodSpec, toleration *v1.Toleration) bool {
293 podTolerations := spec.Tolerations
294
295 var newTolerations []v1.Toleration
296 updated := false
297 for i := range podTolerations {
298 if toleration.MatchToleration(&podTolerations[i]) {
299 if helper.Semantic.DeepEqual(toleration, podTolerations[i]) {
300 return false
301 }
302 newTolerations = append(newTolerations, *toleration)
303 updated = true
304 continue
305 }
306
307 newTolerations = append(newTolerations, podTolerations[i])
308 }
309
310 if !updated {
311 newTolerations = append(newTolerations, *toleration)
312 }
313
314 spec.Tolerations = newTolerations
315 return true
316 }
317
318
319 func GetMatchingTolerations(taints []v1.Taint, tolerations []v1.Toleration) (bool, []v1.Toleration) {
320 if len(taints) == 0 {
321 return true, []v1.Toleration{}
322 }
323 if len(tolerations) == 0 && len(taints) > 0 {
324 return false, []v1.Toleration{}
325 }
326 result := []v1.Toleration{}
327 for i := range taints {
328 tolerated := false
329 for j := range tolerations {
330 if tolerations[j].ToleratesTaint(&taints[i]) {
331 result = append(result, tolerations[j])
332 tolerated = true
333 break
334 }
335 }
336 if !tolerated {
337 return false, []v1.Toleration{}
338 }
339 }
340 return true, result
341 }
342
343
344
345 func ScopedResourceSelectorRequirementsAsSelector(ssr v1.ScopedResourceSelectorRequirement) (labels.Selector, error) {
346 selector := labels.NewSelector()
347 var op selection.Operator
348 switch ssr.Operator {
349 case v1.ScopeSelectorOpIn:
350 op = selection.In
351 case v1.ScopeSelectorOpNotIn:
352 op = selection.NotIn
353 case v1.ScopeSelectorOpExists:
354 op = selection.Exists
355 case v1.ScopeSelectorOpDoesNotExist:
356 op = selection.DoesNotExist
357 default:
358 return nil, fmt.Errorf("%q is not a valid scope selector operator", ssr.Operator)
359 }
360 r, err := labels.NewRequirement(string(ssr.ScopeName), op, ssr.Values)
361 if err != nil {
362 return nil, err
363 }
364 selector = selector.Add(*r)
365 return selector, nil
366 }
367
View as plain text