1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package resourceoverrides
16
17 import (
18 "encoding/json"
19 "fmt"
20 "reflect"
21 "strings"
22
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/crd/crdgeneration/crdboilerplate"
24 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
25 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/crdutil"
26
27 apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
28 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
30 )
31
32
33 func KeepTopLevelFieldOptionalWithDefault(crd *apiextensions.CustomResourceDefinition, defaultValue interface{}, field string) error {
34 schema := k8s.GetOpenAPIV3SchemaFromCRD(crd)
35 spec := schema.Properties["spec"]
36 fieldSchema := spec.Properties[field]
37 bytes, err := json.Marshal(defaultValue)
38 if err != nil {
39 return err
40 }
41 fieldSchema.Default = &apiextensions.JSON{
42 Raw: bytes,
43 }
44 spec.Properties[field] = fieldSchema
45
46 required := make([]string, 0)
47 for _, v := range spec.Required {
48 if v != field {
49 required = append(required, v)
50 }
51 }
52 spec.Required = required
53 schema.Properties["spec"] = spec
54 if len(spec.Required) == 0 {
55 schema.Required = []string{}
56 }
57 return nil
58 }
59
60 func getSchemaForPath(schema *apiextensions.JSONSchemaProps, path []string) (*apiextensions.JSONSchemaProps, error) {
61 if len(path) == 0 {
62 return schema, nil
63 }
64
65 var subSchema apiextensions.JSONSchemaProps
66 var ok bool
67 if schema.Properties != nil {
68 if subSchema, ok = schema.Properties[path[0]]; !ok {
69 return nil, fmt.Errorf("can't find field %s under properties", path[0])
70 }
71 } else if schema.AdditionalProperties != nil {
72
73 return nil, fmt.Errorf("field %s is under a map field: this path can't be handled", path[0])
74 } else {
75 return nil, fmt.Errorf("the schema doesn't support any subfield")
76 }
77
78 if len(path) == 1 {
79 return &subSchema, nil
80 }
81
82 switch subSchema.Type {
83 case "array":
84 return getSchemaForPath(subSchema.Items.Schema, path[1:])
85 default:
86 return getSchemaForPath(&subSchema, path[1:])
87 }
88 }
89
90
91
92 func PreserveMutuallyExclusiveNonReferenceField(crd *apiextensions.CustomResourceDefinition, parentPath []string, referenceFieldName, nonReferenceFieldName string) error {
93 if referenceFieldName == "" || nonReferenceFieldName == "" {
94 return fmt.Errorf("both 'referenceFieldName' and 'nonReferenceFieldName' must be specified")
95 }
96
97
98 schema := k8s.GetOpenAPIV3SchemaFromCRD(crd)
99 var err error
100 var parent *apiextensions.JSONSchemaProps
101
102 if len(parentPath) == 0 {
103 parentPath = []string{"spec"}
104 } else {
105 parentPath = append([]string{"spec"}, parentPath...)
106 }
107 parentPathStr := strings.Join(parentPath, ".")
108 parent, err = getSchemaForPath(schema, parentPath)
109 if err != nil {
110 return fmt.Errorf("can't get schema for path '%v' in CRD %s: %v", parentPathStr, crd.Name, err)
111 }
112
113
114 requiredFields, err := crdutil.GetRequiredRuleForObjectOrArray(parent)
115 if err != nil {
116 return fmt.Errorf("error getting the required rule under path %s for CRD %s: %v", parentPath, crd.Name, err)
117 }
118 var required bool
119 for _, field := range requiredFields {
120 if field == referenceFieldName {
121 required = true
122 break
123 }
124 }
125
126
127
128 if required {
129 oneOfRule, err := crdutil.GetOneOfRuleForObjectOrArray(parent)
130 if err != nil {
131 return fmt.Errorf("error getting the oneOf rule under path %s for CRD %s: %v", parentPath, crd.Name, err)
132 }
133
134 if oneOfRule != nil {
135 return fmt.Errorf("can't handle multiple pairs of required mutually exclustive fields under %s for field %s and %s in CRD %s", parentPath, referenceFieldName, referenceFieldName, crd.Name)
136 }
137
138 oneOfRule = []*apiextensions.JSONSchemaProps{
139 {
140 Required: []string{nonReferenceFieldName},
141 },
142 {
143 Required: []string{referenceFieldName},
144 },
145 }
146 if err := crdutil.SetOneOfRuleForObjectOrArray(parent, oneOfRule); err != nil {
147 return fmt.Errorf("error setting the oneOf rule under path %s for CRD %s: %v", parentPath, crd.Name, err)
148 }
149
150 var updatedRequiredFields []string
151 for _, field := range requiredFields {
152 if field != referenceFieldName {
153 updatedRequiredFields = append(updatedRequiredFields, field)
154 }
155 }
156 if err := crdutil.SetRequiredRuleForObjectOrArray(parent, updatedRequiredFields); err != nil {
157 return fmt.Errorf("error setting the required rule under path %s for CRD %s: %v", parentPath, crd.Name, err)
158 }
159 } else {
160 notRule, err := crdutil.GetNotRuleForObjectOrArray(parent)
161 if err != nil {
162 return fmt.Errorf("error getting the not rule under path %s for CRD %s: %v", parentPath, crd.Name, err)
163 }
164
165 if notRule != nil {
166 return fmt.Errorf("can't handling multiple pairs of optional mutually exclustive fields for %s in %s", referenceFieldName, crd.Name)
167 }
168
169 notRule = &apiextensions.JSONSchemaProps{
170 Required: []string{nonReferenceFieldName, referenceFieldName},
171 }
172 if err := crdutil.SetNotRuleForObjectOrArray(parent, notRule); err != nil {
173 return fmt.Errorf("error setting the not rule under path %s for CRD %s: %v", parentPath, crd.Name, err)
174 }
175 }
176
177
178 referenceFieldSchema, ok, err := crdutil.GetSchemaForFieldUnderObjectOrArray(referenceFieldName, parent)
179 if err != nil {
180 return fmt.Errorf("error getting schema for reference field %s under path %s for CRD %s: %v", referenceFieldName, parentPath, crd.Name, err)
181 }
182 if !ok {
183 return fmt.Errorf("can't find reference field %s under path %s for CRD %s", referenceFieldName, parentPath, crd.Name)
184 }
185 fieldType := referenceFieldSchema.Type
186 if fieldType != "object" && fieldType != "array" {
187 return fmt.Errorf("wrong type for reference field %s under path %s for CRD %s: %s", referenceFieldName, parentPath, crd.Name, fieldType)
188 }
189
190 var nonReferenceFieldSchema *apiextensions.JSONSchemaProps
191 description := fmt.Sprintf("DEPRECATED. Although this field is still available, there is limited support. "+
192 "We recommend that you use `%s.%s` instead.", parentPathStr, referenceFieldName)
193 if fieldType == "object" {
194 nonReferenceFieldSchema = &apiextensions.JSONSchemaProps{
195 Description: description,
196
197
198 Type: "string",
199 }
200 } else if fieldType == "array" {
201 nonReferenceFieldSchema = &apiextensions.JSONSchemaProps{
202 Description: description,
203
204
205 Type: "array",
206 Items: &apiextensions.JSONSchemaPropsOrArray{
207 Schema: &apiextensions.JSONSchemaProps{
208 Type: "string",
209 },
210 },
211 }
212 }
213
214 if err := crdutil.SetSchemaForFieldUnderObjectOrArray(nonReferenceFieldName, parent, nonReferenceFieldSchema); err != nil {
215 return fmt.Errorf("error setting schema for non-reference field %s under path %s for CRD %s: %v", nonReferenceFieldName, parentPath, crd.Name, err)
216 }
217
218
219 updatedSchema, err := getSchemaForPath(schema, parentPath[:len(parentPath)-1])
220 if err != nil {
221 return fmt.Errorf("can't get schema for path '%v' in CRD %s: %v", parentPathStr, crd.Name, err)
222 }
223 if err := crdutil.SetSchemaForFieldUnderObjectOrArray(parentPath[len(parentPath)-1], updatedSchema, parent); err != nil {
224 return fmt.Errorf("error setting updated schema for parent path '%v' for CRD %s: %v", parentPathStr, crd.Name, err)
225 }
226
227 return nil
228 }
229
230
231
232 func EnsureReferenceFieldIsMultiKind(crd *apiextensions.CustomResourceDefinition, parentPath []string, referenceFieldName string, supportedKinds []string) error {
233 if referenceFieldName == "" {
234 return fmt.Errorf("param 'referenceFieldName' must be specified")
235 }
236
237
238 schema := k8s.GetOpenAPIV3SchemaFromCRD(crd)
239 var err error
240 var parent *apiextensions.JSONSchemaProps
241
242 if len(parentPath) == 0 {
243 parentPath = []string{"spec"}
244 } else {
245 parentPath = append([]string{"spec"}, parentPath...)
246 }
247 parentPathStr := strings.Join(parentPath, ".")
248 parent, err = getSchemaForPath(schema, parentPath)
249 if err != nil {
250 return fmt.Errorf("can't get schema for path '%v' in CRD %s: %v", parentPathStr, crd.Name, err)
251 }
252
253
254
255 referenceFieldSchema, ok, err := crdutil.GetSchemaForFieldUnderObjectOrArray(referenceFieldName, parent)
256 if err != nil {
257 return fmt.Errorf("error getting schema for reference field %s under path %s for CRD %s: %v", referenceFieldName, parentPath, crd.Name, err)
258 }
259 if !ok {
260 return fmt.Errorf("can't find reference field %s under path %s for CRD %s", referenceFieldName, parentPath, crd.Name)
261 }
262 fieldType := referenceFieldSchema.Type
263 if fieldType != "object" && fieldType != "array" {
264 return fmt.Errorf("wrong type for reference field %s under path %s for CRD %s: %s", referenceFieldName, parentPath, crd.Name, fieldType)
265 }
266
267
268
269 referenceFieldSchemaForSubfields := referenceFieldSchema
270 if fieldType == "array" {
271 referenceFieldSchemaForSubfields = referenceFieldSchema.Items.Schema
272 }
273 if _, ok := referenceFieldSchemaForSubfields.Properties["kind"]; !ok {
274 externalRefSchema, ok := referenceFieldSchemaForSubfields.Properties["external"]
275 if !ok {
276 return fmt.Errorf("can't find external field under reference %s in CRD %s", referenceFieldName, crd.Name)
277 }
278
279 if len(supportedKinds) == 0 {
280 return fmt.Errorf("there must be at least one kind specified in 'supportedKinds' list")
281 }
282 referenceFieldSchemaWithKind := crdboilerplate.GetMultiKindResourceReferenceSchemaBoilerplate(externalRefSchema.Description, supportedKinds)
283
284 if fieldType == "array" {
285 referenceFieldSchema.Items.Schema = referenceFieldSchemaWithKind
286 } else if fieldType == "object" {
287 referenceFieldSchema = referenceFieldSchemaWithKind
288 }
289 if err := crdutil.SetSchemaForFieldUnderObjectOrArray(referenceFieldName, parent, referenceFieldSchema); err != nil {
290 return fmt.Errorf("error setting schema for reference field %s under path %s for CRD %s: %v", referenceFieldName, parentPath, crd.Name, err)
291 }
292 }
293
294
295 updatedSchema, err := getSchemaForPath(schema, parentPath[:len(parentPath)-1])
296 if err != nil {
297 return fmt.Errorf("can't get schema for path '%v' in CRD %s: %v", parentPathStr, crd.Name, err)
298 }
299 if err := crdutil.SetSchemaForFieldUnderObjectOrArray(parentPath[len(parentPath)-1], updatedSchema, parent); err != nil {
300 return fmt.Errorf("error setting updated schema for parent path '%v' for CRD %s: %v", parentPathStr, crd.Name, err)
301 }
302
303 return nil
304 }
305
306
307
308 func PruneNoOpsField(r *k8s.Resource, path ...string) error {
309 unstructured.RemoveNestedField(r.Spec, path...)
310 return nil
311 }
312
313
314
315 func PreserveUserSpecifiedLegacyField(original, reconciled *k8s.Resource, path ...string) error {
316 vo, found, err := unstructured.NestedFieldCopy(original.Spec, path...)
317 if err != nil {
318 return err
319 }
320 if !found {
321 return nil
322 }
323 if err := unstructured.SetNestedField(reconciled.Spec, vo, path...); err != nil {
324 return err
325 }
326 return nil
327 }
328
329
330
331
332
333
334 func PreserveUserSpecifiedLegacyFieldUnderSlice(original, reconciled *k8s.Resource, upToSlicePath []string, path []string) error {
335 originalSlice, foundOriginal, err := unstructured.NestedSlice(original.Spec, upToSlicePath...)
336 if err != nil {
337 return fmt.Errorf("error getting the nested slice under path %s for the original resource: %v", strings.Join(upToSlicePath, "."), err)
338 }
339 reconciledSlice, foundReconciled, err := unstructured.NestedSlice(reconciled.Spec, upToSlicePath...)
340 if err != nil {
341 return fmt.Errorf("error getting the nested slice under path %s for the reconciled resource: %v", strings.Join(upToSlicePath, "."), err)
342 }
343 if !foundOriginal || !foundReconciled {
344 return nil
345 }
346 for i, v := range originalSlice {
347 pathFieldValue, foundPathField, err := unstructured.NestedFieldCopy(v.(map[string]interface{}), path...)
348 if err != nil {
349 return fmt.Errorf("error getting the user-specified value from the path %s within the slice field: %v", strings.Join(path, "."), err)
350 }
351 if !foundPathField {
352 continue
353 }
354 if err := unstructured.SetNestedField(reconciledSlice[i].(map[string]interface{}), pathFieldValue, path...); err != nil {
355 return fmt.Errorf("error setting original value to reconciled slice element: %v", err)
356 }
357 }
358 if err := unstructured.SetNestedSlice(reconciled.Spec, reconciledSlice, upToSlicePath...); err != nil {
359 return fmt.Errorf("error setting preserved values back into reconciled object: %v", err)
360 }
361 return nil
362 }
363
364
365
366
367
368
369 func PreserveUserSpecifiedLegacyArrayField(original, reconciled *k8s.Resource, path ...string) error {
370 vo, found, err := unstructured.NestedSlice(original.Spec, path...)
371 if err != nil {
372 return err
373 }
374 if !found {
375 return nil
376 }
377 if err := unstructured.SetNestedSlice(reconciled.Spec, vo, path...); err != nil {
378 return err
379 }
380 return nil
381 }
382
383
384
385
386
387 func PruneDefaultedAuthoritativeFieldIfOnlyLegacyFieldSpecified(original, reconciled *k8s.Resource, legacyFieldPath, fieldPath []string) error {
388
389 _, found, err := unstructured.NestedFieldCopy(original.Spec, fieldPath...)
390 if err != nil {
391 return err
392 }
393 if found {
394 return nil
395 }
396
397 _, found, err = unstructured.NestedFieldCopy(original.Spec, legacyFieldPath...)
398 if err != nil {
399 return err
400 }
401 if found {
402 unstructured.RemoveNestedField(reconciled.Spec, fieldPath...)
403 return nil
404 }
405 return nil
406 }
407
408
409
410
411
412
413
414 func PruneDefaultedAuthoritativeFieldIfOnlyLegacyFieldSpecifiedUnderSlice(original, reconciled *k8s.Resource, pathUpToSlice, nonReferenceFieldPath, referenceFieldPath []string) error {
415 originalSlice, foundOriginal, err := unstructured.NestedSlice(original.Spec, pathUpToSlice...)
416 if err != nil {
417 return fmt.Errorf("error getting the nested slice under path %s for the original resource: %v", strings.Join(pathUpToSlice, "."), err)
418 }
419 reconciledSlice, foundReconciled, err := unstructured.NestedSlice(reconciled.Spec, pathUpToSlice...)
420 if err != nil {
421 return fmt.Errorf("error getting the nested slice under path %s for the reconciled resource: %v", strings.Join(pathUpToSlice, "."), err)
422 }
423 if !foundOriginal || !foundReconciled {
424 return nil
425 }
426 for i, v := range originalSlice {
427 _, foundReferenceField, err := unstructured.NestedFieldCopy(v.(map[string]interface{}), referenceFieldPath...)
428 if err != nil {
429 return fmt.Errorf("error checking the existence of the nested reference field %s within the slice field of the original resource: %v", strings.Join(referenceFieldPath, "."), err)
430 }
431 _, foundNonReferenceField, err := unstructured.NestedFieldCopy(v.(map[string]interface{}), nonReferenceFieldPath...)
432 if err != nil {
433 return fmt.Errorf("error checking the existence of the nested non-reference field %s within the slice field of the original resource: %v", strings.Join(nonReferenceFieldPath, "."), err)
434 }
435 if !foundReferenceField && foundNonReferenceField {
436 unstructured.RemoveNestedField(reconciledSlice[i].(map[string]interface{}), referenceFieldPath...)
437 }
438 }
439 if err := unstructured.SetNestedSlice(reconciled.Spec, reconciledSlice, pathUpToSlice...); err != nil {
440 return fmt.Errorf("error setting the altered slice field %s back into the reconciled resource: %v", strings.Join(pathUpToSlice, "."), err)
441 }
442 return nil
443 }
444
445
446
447
448
449
450
451
452
453 func PruneDefaultedAuthoritativeArrayFieldIfOnlyLegacyArrayFieldSpecified(original, reconciled *k8s.Resource, legacyFieldPath, fieldPath []string) error {
454
455 _, found, err := unstructured.NestedSlice(original.Spec, fieldPath...)
456 if err != nil {
457 return err
458 }
459 if found {
460 return nil
461 }
462
463
464
465 _, found, err = unstructured.NestedSlice(original.Spec, legacyFieldPath...)
466 if err != nil {
467 return err
468 }
469 if found {
470 unstructured.RemoveNestedField(reconciled.Spec, fieldPath...)
471 }
472 return nil
473 }
474
475
476
477
478 func FavorAuthoritativeFieldOverLegacyField(r *k8s.Resource, legacyFieldPath, fieldPath []string) error {
479 if err := validateIfAuthoritativeFieldAndLegacyFieldTakeDifferentValues(r, legacyFieldPath, fieldPath); err != nil {
480 return err
481 }
482
483
484 _, found, err := unstructured.NestedFieldCopy(r.Spec, fieldPath...)
485 if err != nil {
486 return err
487 }
488 if found {
489 unstructured.RemoveNestedField(r.Spec, legacyFieldPath...)
490 return nil
491 }
492
493
494 v, found, err := unstructured.NestedFieldCopy(r.Spec, legacyFieldPath...)
495 if err != nil {
496 return err
497 }
498 if !found {
499 return nil
500 }
501 if isReferenceFieldPath(fieldPath) {
502 if err := unstructured.SetNestedField(r.Spec, v, append(fieldPath, "external")...); err != nil {
503 return err
504 }
505
506 if err := markFieldAsManaged(r, append(fieldPath, "external")...); err != nil {
507 return err
508 }
509
510 if err := markFieldAsManaged(r, fieldPath...); err != nil {
511 return err
512 }
513 unstructured.RemoveNestedField(r.Spec, legacyFieldPath...)
514 return nil
515 }
516 if err := unstructured.SetNestedField(r.Spec, v, fieldPath...); err != nil {
517 return err
518 }
519
520 if err := markFieldAsManaged(r, fieldPath...); err != nil {
521 return err
522 }
523 unstructured.RemoveNestedField(r.Spec, legacyFieldPath...)
524 return nil
525 }
526
527
528 func isReferenceFieldPath(fieldPath []string) bool {
529 field := fieldPath[len(fieldPath)-1]
530 return strings.HasSuffix(field, "Ref")
531 }
532
533
534
535 func FavorReferenceFieldOverNonReferenceFieldUnderSlice(r *k8s.Resource, pathUpToSlice, nonReferenceFieldPath, referenceFieldPath []string) error {
536 if err := validateAtMostOneFieldIsSetUnderSlice(r, pathUpToSlice, nonReferenceFieldPath, referenceFieldPath); err != nil {
537 return err
538 }
539 sliceVal, found, err := unstructured.NestedSlice(r.Spec, pathUpToSlice...)
540 if err != nil {
541 return fmt.Errorf("error getting nested slice field %s from resource: %v", strings.Join(pathUpToSlice, "."), err)
542 }
543 if !found {
544 return nil
545 }
546 for i, v := range sliceVal {
547 nonReferenceVal, found, err := unstructured.NestedFieldCopy(v.(map[string]interface{}), nonReferenceFieldPath...)
548 if err != nil {
549 return fmt.Errorf("error getting non-reference field %s from slice field: %v", strings.Join(nonReferenceFieldPath, "."), err)
550 }
551 if !found {
552 continue
553 }
554 if err := unstructured.SetNestedField(sliceVal[i].(map[string]interface{}), nonReferenceVal, append(referenceFieldPath, "external")...); err != nil {
555 return fmt.Errorf("error setting non-reference value to reference field path %s: %v", strings.Join(append(referenceFieldPath, "external"), "."), err)
556 }
557 unstructured.RemoveNestedField(sliceVal[i].(map[string]interface{}), nonReferenceFieldPath...)
558 }
559 if err := unstructured.SetNestedSlice(r.Spec, sliceVal, pathUpToSlice...); err != nil {
560 return fmt.Errorf("error setting altered slice %s back into resource: %v", strings.Join(pathUpToSlice, "."), err)
561 }
562 return nil
563 }
564
565
566
567 func validateIfAuthoritativeFieldAndLegacyFieldTakeDifferentValues(r *k8s.Resource, legacyFieldPath, fieldPath []string) error {
568 v1, f1, err := unstructured.NestedFieldCopy(r.Spec, fieldPath...)
569 if err != nil {
570 return err
571 }
572
573 v2, f2, err := unstructured.NestedFieldCopy(r.Spec, legacyFieldPath...)
574 if err != nil {
575 return err
576 }
577
578 if f1 && f2 && !reflect.DeepEqual(v1, v2) {
579 authoritative := strings.Join(fieldPath, ".")
580 legacy := strings.Join(legacyFieldPath, ".")
581 return fmt.Errorf("'%v' field and '%v' field are both present in spec, but they take different values. It's recommended to only use '%v' in your configuration because '%v' has been deprecated by the API",
582 authoritative, legacy, authoritative, legacy)
583 }
584 return nil
585 }
586
587
588 func validateAtMostOneFieldIsSetUnderSlice(r *k8s.Resource, fieldPathUpToSlice, fieldPath1, fieldPath2 []string) error {
589 sliceField, found, err := unstructured.NestedSlice(r.Spec, fieldPathUpToSlice...)
590 if err != nil {
591 return fmt.Errorf("error getting slice field %s from resource: %v", strings.Join(fieldPathUpToSlice, "."), err)
592 }
593 if !found {
594 return nil
595 }
596 for _, v := range sliceField {
597 _, found1, err := unstructured.NestedFieldCopy(v.(map[string]interface{}), fieldPath1...)
598 if err != nil {
599 return fmt.Errorf("error checking existence of nested field %s within slice field: %v", strings.Join(fieldPath1, "."), err)
600 }
601 _, found2, err := unstructured.NestedFieldCopy(v.(map[string]interface{}), fieldPath2...)
602 if err != nil {
603 return fmt.Errorf("error checking existence of nested field %s within slice field: %v", strings.Join(fieldPath2, "."), err)
604 }
605 if !found1 || !found2 {
606 continue
607 }
608 fullFieldPath1 := strings.Join(append(fieldPathUpToSlice, fieldPath1...), ".")
609 fullFieldPath2 := strings.Join(append(fieldPathUpToSlice, fieldPath2...), ".")
610 return fmt.Errorf("'%v' field and '%v' field are both set. Please remove one of the two fields", fullFieldPath1, fullFieldPath2)
611 }
612 return nil
613 }
614
615 func markFieldAsManaged(r *k8s.Resource, fieldPath ...string) error {
616 if r.ManagedFields == nil {
617 return nil
618 }
619 parts := make([]interface{}, 0, len(fieldPath))
620 for _, v := range fieldPath {
621 parts = append(parts, v)
622 }
623 p, err := fieldpath.MakePath(parts...)
624 if err != nil {
625 return err
626 }
627 r.ManagedFields.Insert(p)
628 return nil
629 }
630
631
632
633
634
635 func FavorReferenceArrayFieldOverNonReferenceArrayField(r *k8s.Resource, nonReferenceFieldPath, referenceFieldPath []string) error {
636 nonRefArray, foundNonRef, err := unstructured.NestedStringSlice(r.Spec, nonReferenceFieldPath...)
637 if err != nil {
638 return fmt.Errorf("error getting the non-reference field '%v': %v", strings.Join(nonReferenceFieldPath, "."), err)
639 }
640 _, foundRef, err := unstructured.NestedSlice(r.Spec, referenceFieldPath...)
641 if err != nil {
642 return fmt.Errorf("error getting the reference field '%v': %v", strings.Join(referenceFieldPath, "."), err)
643 }
644
645 if foundNonRef && foundRef {
646 return fmt.Errorf("mutually-exclusive fields '%v' and '%v' are both set", strings.Join(nonReferenceFieldPath, "."), strings.Join(referenceFieldPath, "."))
647 }
648
649 if !foundNonRef || len(nonRefArray) == 0 {
650 return nil
651 }
652
653
654
655
656 refArray := make([]interface{}, len(nonRefArray))
657 for i, val := range nonRefArray {
658 refItem := make(map[string]interface{})
659 if err := unstructured.SetNestedField(refItem, val, "external"); err != nil {
660 return fmt.Errorf("error setting the 'external' field under the reference array '%v': %v", strings.Join(referenceFieldPath, "."), err)
661 }
662 refArray[i] = refItem
663 }
664 if err := unstructured.SetNestedSlice(r.Spec, refArray, referenceFieldPath...); err != nil {
665 return err
666 }
667 unstructured.RemoveNestedField(r.Spec, nonReferenceFieldPath...)
668 return nil
669 }
670
671
672
673
674
675
676 func RemovePrefixFromStringFieldInSpec(r *k8s.Resource, prefix string, path ...string) error {
677 vo, found, err := unstructured.NestedString(r.Spec, path...)
678 if err != nil {
679 return err
680 }
681 if !found {
682 return nil
683 }
684 if !strings.HasPrefix(vo, prefix) {
685 return nil
686 }
687 v := strings.TrimPrefix(vo, prefix)
688 if err := unstructured.SetNestedField(r.Spec, v, path...); err != nil {
689 return err
690 }
691 return nil
692 }
693
View as plain text