1 package object
2
3 import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "strings"
8
9 "github.com/google/go-cmp/cmp"
10 appsv1 "k8s.io/api/apps/v1"
11 hpav2beta1 "k8s.io/api/autoscaling/v2beta1"
12 hpav2beta2 "k8s.io/api/autoscaling/v2beta2"
13 corev1 "k8s.io/api/core/v1"
14 apiequality "k8s.io/apimachinery/pkg/api/equality"
15 "k8s.io/apimachinery/pkg/api/errors"
16 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
17 "k8s.io/apimachinery/pkg/runtime"
18 yamlutil "k8s.io/apimachinery/pkg/util/yaml"
19 "sigs.k8s.io/cli-utils/pkg/object"
20 "sigs.k8s.io/controller-runtime/pkg/client"
21 "sigs.k8s.io/yaml"
22 )
23
24 const (
25 fmtSeparator = "/"
26 Secret = "Secret"
27 DefaultMask = "*****"
28 DiffMask = "******"
29 )
30
31
32 func FmtMetadata(obj object.ObjMetadata) string {
33 var builder strings.Builder
34 builder.WriteString(obj.GroupKind.Kind + fmtSeparator)
35 if obj.Namespace != "" {
36 builder.WriteString(obj.Namespace + fmtSeparator)
37 }
38 builder.WriteString(obj.Name)
39 return builder.String()
40 }
41
42
43 func FmtUnstructured(obj *unstructured.Unstructured) string {
44 return FmtMetadata(object.UnstructuredToObjMetadata(obj))
45 }
46
47
48 func FmtUnstructuredList(objects []*unstructured.Unstructured) string {
49 var b strings.Builder
50 for _, obj := range objects {
51 b.WriteString(FmtMetadata(object.UnstructuredToObjMetadata(obj)) + "\n")
52 }
53 return strings.TrimSuffix(b.String(), "\n")
54 }
55
56 func FmtObject(obj client.Object) string {
57 var builder strings.Builder
58 builder.WriteString(obj.GetObjectKind().GroupVersionKind().Kind + fmtSeparator)
59 if obj.GetNamespace() != "" {
60 builder.WriteString(obj.GetNamespace() + fmtSeparator)
61 }
62 builder.WriteString(obj.GetName())
63 return builder.String()
64 }
65
66 func FmtObjectList(objs []client.Object) string {
67 var b strings.Builder
68 for _, obj := range objs {
69 b.WriteString(FmtObject(obj) + "\n")
70 }
71 return strings.TrimSuffix(b.String(), "\n")
72 }
73
74 func GetNestedMap(object *unstructured.Unstructured) (map[string]interface{}, bool, error) {
75 dryRunData, foundDryRun, err := unstructured.NestedMap(object.Object, "data")
76 if err != nil {
77 return nil, foundDryRun, err
78 }
79
80 return dryRunData, foundDryRun, nil
81 }
82
83 func SetNestedMap(object *unstructured.Unstructured, data map[string]interface{}) error {
84 err := unstructured.SetNestedMap(object.Object, data, "data")
85 if err != nil {
86 return err
87 }
88
89 return nil
90 }
91
92 func CmpMaskData(currentData, futureData map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
93 for k, currentVal := range currentData {
94 futureVal, ok := futureData[k]
95 if !ok {
96
97 currentData[k] = DefaultMask
98 continue
99 }
100
101 if cmp.Diff(currentVal, futureVal) != "" {
102
103 currentData[k] = DefaultMask
104 futureData[k] = DiffMask
105 continue
106 }
107
108 currentData[k] = DefaultMask
109 futureData[k] = DefaultMask
110 }
111
112 for k := range futureData {
113 if _, ok := currentData[k]; !ok {
114
115 futureData[k] = DefaultMask
116 }
117 }
118
119 return currentData, futureData
120 }
121
122
123 func MaskSecret(data map[string]interface{}, object *unstructured.Unstructured, mask string) (*unstructured.Unstructured, error) {
124 for k := range data {
125 data[k] = mask
126 }
127
128 err := SetNestedMap(object, data)
129 if err != nil {
130 return nil, err
131 }
132
133 return object, err
134 }
135
136
137 func ReadObject(r io.Reader) (*unstructured.Unstructured, error) {
138 reader := yamlutil.NewYAMLOrJSONDecoder(r, 2048)
139 obj := &unstructured.Unstructured{}
140 err := reader.Decode(obj)
141 if err != nil {
142 return nil, err
143 }
144
145 return obj, nil
146 }
147
148
149
150 func ReadObjects(r io.Reader) ([]*unstructured.Unstructured, error) {
151 reader := yamlutil.NewYAMLOrJSONDecoder(r, 2048)
152 objects := make([]*unstructured.Unstructured, 0)
153
154 for {
155 obj := &unstructured.Unstructured{}
156 err := reader.Decode(obj)
157 if err != nil {
158 if err == io.EOF {
159 break
160 }
161 return objects, err
162 }
163
164 if obj.IsList() {
165 err = obj.EachListItem(func(item runtime.Object) error {
166 obj := item.(*unstructured.Unstructured)
167 objects = append(objects, obj)
168 return nil
169 })
170 if err != nil {
171 return objects, err
172 }
173 continue
174 }
175
176 if IsKubernetesObject(obj) && !IsKustomization(obj) {
177 objects = append(objects, obj)
178 }
179 }
180
181 return objects, nil
182 }
183
184
185 func ToYAML(object *unstructured.Unstructured) string {
186 var builder strings.Builder
187 data, err := yaml.Marshal(object)
188 if err != nil {
189 return ""
190 }
191 builder.Write(data)
192 builder.WriteString("---\n")
193
194 return builder.String()
195 }
196
197
198 func ObjectsToYAML(objects []*unstructured.Unstructured) (string, error) {
199 var builder strings.Builder
200 for _, obj := range objects {
201 data, err := yaml.Marshal(obj)
202 if err != nil {
203 return "", err
204 }
205 builder.Write(data)
206 builder.WriteString("---\n")
207 }
208 return builder.String(), nil
209 }
210
211
212 func ToJSON(objects []*unstructured.Unstructured) (string, error) {
213 list := struct {
214 APIVersion string `json:"apiVersion,omitempty"`
215 Kind string `json:"kind,omitempty"`
216 Items []*unstructured.Unstructured `json:"items,omitempty"`
217 }{
218 APIVersion: "v1",
219 Kind: "ListMeta",
220 Items: objects,
221 }
222
223 data, err := json.MarshalIndent(list, "", " ")
224 if err != nil {
225 return "", err
226 }
227
228 return string(data), nil
229 }
230
231
232 func IsClusterDefinition(object *unstructured.Unstructured) bool {
233 kind := object.GetKind()
234 switch strings.ToLower(kind) {
235 case "customresourcedefinition":
236 return true
237 case "namespace":
238 return true
239 }
240 return false
241 }
242
243
244 func IsKubernetesObject(object *unstructured.Unstructured) bool {
245 if object.GetName() == "" || object.GetKind() == "" || object.GetAPIVersion() == "" {
246 return false
247 }
248 return true
249 }
250
251
252 func IsKustomization(object *unstructured.Unstructured) bool {
253 if object.GetKind() == "Kustomization" && object.GroupVersionKind().GroupKind().Group == "kustomize.config.k8s.io" {
254 return true
255 }
256 return false
257 }
258
259
260
261 func AnyInMetadata(object *unstructured.Unstructured, metadata map[string]string) bool {
262 for key, val := range metadata {
263 if object.GetLabels()[key] == val || object.GetAnnotations()[key] == val {
264 return true
265 }
266 }
267 return false
268 }
269
270
271
272
273
274
275
276 func IsDeleted(object client.Object, err error) bool {
277 return !object.GetDeletionTimestamp().IsZero() ||
278 errors.IsNotFound(err) ||
279 errors.IsGone(err)
280 }
281
282
283
284
285
286
287 func SetNativeKindsDefaults(objects []*unstructured.Unstructured) error {
288 var setProtoDefault = func(spec *corev1.PodSpec) {
289 for _, c := range spec.Containers {
290 for i, port := range c.Ports {
291 if port.Protocol == "" {
292 c.Ports[i].Protocol = "TCP"
293 }
294 }
295 }
296 }
297 for _, u := range objects {
298 switch u.GetAPIVersion() {
299 case "v1":
300 switch u.GetKind() {
301 case "Service":
302 var d corev1.Service
303 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
304 if err != nil {
305 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
306 }
307
308
309
310 for i, port := range d.Spec.Ports {
311 if port.Protocol == "" {
312 d.Spec.Ports[i].Protocol = "TCP"
313 }
314 }
315
316 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
317 if err != nil {
318 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
319 }
320 u.Object = out
321 case "Pod":
322 var d corev1.Pod
323 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
324 if err != nil {
325 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
326 }
327
328 setProtoDefault(&d.Spec)
329
330 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
331 if err != nil {
332 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
333 }
334 u.Object = out
335 case "Secret":
336 var s corev1.Secret
337 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &s)
338 if err != nil {
339 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
340 }
341 convertStringDataToData(&s)
342 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&s)
343 if err != nil {
344 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
345 }
346 u.Object = out
347 }
348
349 case "apps/v1":
350 switch u.GetKind() {
351 case "Deployment":
352 var d appsv1.Deployment
353 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
354 if err != nil {
355 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
356 }
357
358 setProtoDefault(&d.Spec.Template.Spec)
359
360 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
361 if err != nil {
362 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
363 }
364 u.Object = out
365 case "StatefulSet":
366 var d appsv1.StatefulSet
367 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
368 if err != nil {
369 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
370 }
371
372 setProtoDefault(&d.Spec.Template.Spec)
373
374 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
375 if err != nil {
376 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
377 }
378 u.Object = out
379 case "DaemonSet":
380 var d appsv1.DaemonSet
381 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
382 if err != nil {
383 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
384 }
385
386 setProtoDefault(&d.Spec.Template.Spec)
387
388 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
389 if err != nil {
390 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
391 }
392 u.Object = out
393 case "ReplicaSet":
394 var d appsv1.ReplicaSet
395 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
396 if err != nil {
397 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
398 }
399
400 setProtoDefault(&d.Spec.Template.Spec)
401
402 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
403 if err != nil {
404 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
405 }
406 u.Object = out
407 }
408 }
409
410 switch u.GetKind() {
411 case "HorizontalPodAutoscaler":
412 switch u.GetAPIVersion() {
413 case "autoscaling/v2beta1":
414 var d hpav2beta1.HorizontalPodAutoscaler
415 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
416 if err != nil {
417 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
418 }
419 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
420 if err != nil {
421 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
422 }
423 u.Object = out
424 case "autoscaling/v2beta2":
425 var d hpav2beta2.HorizontalPodAutoscaler
426 err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
427 if err != nil {
428 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
429 }
430 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
431 if err != nil {
432 return fmt.Errorf("%s validation error: %w", FmtUnstructured(u), err)
433 }
434 u.Object = out
435 }
436 }
437
438
439 unstructured.RemoveNestedField(u.Object, "metadata", "creationTimestamp")
440
441
442 if u.GetKind() != "CustomResourceDefinition" {
443 unstructured.RemoveNestedField(u.Object, "status")
444 }
445 }
446 return nil
447 }
448
449
450
451 func FixHorizontalPodAutoscaler(object *unstructured.Unstructured) error {
452 if object.GetKind() == "HorizontalPodAutoscaler" {
453 switch object.GetAPIVersion() {
454 case "autoscaling/v2beta2":
455 var d hpav2beta2.HorizontalPodAutoscaler
456 err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, &d)
457 if err != nil {
458 return fmt.Errorf("%s validation error: %w", FmtUnstructured(object), err)
459 }
460
461 var metrics []hpav2beta2.MetricSpec
462 for _, metric := range d.Spec.Metrics {
463 found := false
464 for _, existing := range metrics {
465 if apiequality.Semantic.DeepEqual(metric, existing) {
466 found = true
467 break
468 }
469 }
470 if !found && metric.Type != "" {
471 metrics = append(metrics, metric)
472 }
473 }
474
475 d.Spec.Metrics = metrics
476
477 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&d)
478 if err != nil {
479 return fmt.Errorf("%s validation error: %w", FmtUnstructured(object), err)
480 }
481 object.Object = out
482 }
483 }
484 return nil
485 }
486
487 func ContainsItemString(s []string, e string) bool {
488 for _, a := range s {
489 if a == e {
490 return true
491 }
492 }
493 return false
494 }
495
496 func convertStringDataToData(secret *corev1.Secret) {
497
498 if len(secret.StringData) > 0 {
499 if secret.Data == nil {
500 secret.Data = map[string][]byte{}
501 }
502 for k, v := range secret.StringData {
503 secret.Data[k] = []byte(v)
504 }
505
506 secret.StringData = nil
507 }
508 }
509
View as plain text