1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package conversion
16
17 import (
18 "fmt"
19 "strings"
20
21 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl"
22 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/extension"
23 dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
24 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/schema/dclschemaloader"
25 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
26 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/label"
27 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/pathslice"
28
29 dclunstruct "github.com/GoogleCloudPlatform/declarative-resource-client-library/unstructured"
30 "github.com/nasa9084/go-openapi"
31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32 )
33
34
35 type Converter struct {
36 SchemaLoader dclschemaloader.DCLSchemaLoader
37 MetadataLoader dclmetadata.ServiceMetadataLoader
38 }
39
40
41 func New(schemaLoader dclschemaloader.DCLSchemaLoader, metadataLoader dclmetadata.ServiceMetadataLoader) *Converter {
42 c := &Converter{}
43 c.SchemaLoader = schemaLoader
44 c.MetadataLoader = metadataLoader
45 return c
46 }
47
48
49 func (c *Converter) KRMObjectToDCLObject(obj *unstructured.Unstructured) (*dclunstruct.Resource, error) {
50 gvk := obj.GroupVersionKind()
51 stv, err := dclmetadata.ToServiceTypeVersion(gvk, c.MetadataLoader)
52 if err != nil {
53 return nil, fmt.Errorf("error resolving the DCL ServiceTypeVersion from GroupVersionKind %v: %w", gvk, err)
54 }
55 resourceMetadata, found := c.MetadataLoader.GetResourceWithGVK(gvk)
56 if !found {
57 return nil, fmt.Errorf("ServiceMetadata for resource with GroupVersionKind %v not found", gvk)
58 }
59
60 dclSchema, err := c.SchemaLoader.GetDCLSchema(stv)
61 if err != nil {
62 return nil, fmt.Errorf("error getting the DCL schema for ServiceTypeVersion %v: %w", stv, err)
63 }
64 if dclSchema.Type != "object" {
65 return nil, fmt.Errorf("expect the entry level DCL OpenAPI schema to be 'object' type, but got %v", dclSchema.Type)
66 }
67
68 r := &dclunstruct.Resource{
69 STV: stv,
70 }
71
72 spec := obj.UnstructuredContent()["spec"]
73 if spec == nil {
74 spec = make(map[string]interface{})
75 }
76 dclObj, err := convertToDCL(spec, []string{}, dclSchema, c.MetadataLoader, false)
77 if err != nil {
78 return nil, fmt.Errorf("error converting the spec of resource %v/%v to DCL object: %w", obj.GetNamespace(), obj.GetName(), err)
79 }
80 dclObjMap, ok := dclObj.(map[string]interface{})
81 if !ok {
82 return nil, fmt.Errorf("expected the converted DCL object to be map[string]interface{} but was actually %T", dclObj)
83 }
84 r.Object = dclObjMap
85
86 if err := convertToDCLNameField(obj, r, dclSchema); err != nil {
87 return nil, fmt.Errorf("error resolving the value of 'name' field for resource %v/%v: %w", obj.GetNamespace(), obj.GetName(), err)
88 }
89
90
91 if !resourceMetadata.SupportsHierarchicalReferences {
92 if err := convertToDCLContainerField(obj, r, dclSchema); err != nil {
93 return nil, fmt.Errorf("error resolving the value of the container field for resource %v/%v: %w", obj.GetNamespace(), obj.GetName(), err)
94 }
95 }
96 if err := convertToDCLLabelsField(obj, r, dclSchema); err != nil {
97 return nil, fmt.Errorf("error converting Kubernetes labels into DCL labels for resource %v/%v: %w", obj.GetNamespace(), obj.GetName(), err)
98 }
99 return r, nil
100 }
101
102
103 func (c *Converter) DCLObjectToKRMObject(resource *dclunstruct.Resource) (*unstructured.Unstructured, error) {
104 obj := &unstructured.Unstructured{
105 Object: make(map[string]interface{}),
106 }
107 gvk, err := dclmetadata.ToGroupVersionKind(resource.STV, c.MetadataLoader)
108 if err != nil {
109 return nil, fmt.Errorf("error resolving GroupVersionKind from the DCL ServiceTypeVersion %v: %w", resource.STV, err)
110 }
111 obj.SetGroupVersionKind(gvk)
112 resourceMetadata, found := c.MetadataLoader.GetResourceWithGVK(gvk)
113 if !found {
114 return nil, fmt.Errorf("ServiceMetadata for resource with GroupVersionKind %v not found", gvk)
115 }
116 dclSchema, err := c.SchemaLoader.GetDCLSchema(resource.STV)
117 if err != nil {
118 return nil, fmt.Errorf("error getting the DCL schema for ServiceTypeVersion %v", resource.STV)
119 }
120 if dclSchema.Type != "object" {
121 return nil, fmt.Errorf("expect the entry level DCL OpenAPI schema to be object type, but got %v", dclSchema.Type)
122 }
123
124
125 dcl.TrimNilFields(resource.Object)
126
127 spec, err := convertToKRMSpec(resource.Object, []string{}, dclSchema, c.MetadataLoader, resourceMetadata, false)
128 if err != nil {
129 return nil, fmt.Errorf("error extracting the spec from the DCL resource %v/%v: %w", obj.GetNamespace(), obj.GetName(), err)
130 }
131 if spec != nil {
132 specMap, ok := spec.(map[string]interface{})
133 if !ok {
134 return nil, fmt.Errorf("expected the converted spec to be map[string]interface{} but was actually %T", spec)
135 }
136 if len(specMap) > 0 {
137 obj.Object["spec"] = specMap
138 }
139 }
140 status, err := convertToKRMStatus(resource.Object, dclSchema)
141 if err != nil {
142 return nil, fmt.Errorf("error extracting the status from the DCL resource %v/%v: %w", obj.GetNamespace(), obj.GetName(), err)
143 }
144 if len(status) > 0 {
145 obj.Object["status"] = status
146 }
147
148 if err := liftDCLLabelsField(obj, dclSchema); err != nil {
149 return nil, fmt.Errorf("error lifting 'labels' field to metadata.labels: %w", err)
150 }
151
152
153 if !resourceMetadata.SupportsHierarchicalReferences {
154 if err := liftDCLContainerField(obj, dclSchema); err != nil {
155 return nil, fmt.Errorf("error lifting contianer field to annotation: %w", err)
156 }
157 }
158 if err := convertToKRMResourceIDField(obj, dclSchema); err != nil {
159 return nil, fmt.Errorf("error converting 'name' field to 'resourceID': %w", err)
160 }
161 return obj, nil
162 }
163
164 func convertToKRMSpec(val interface{}, path []string, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader,
165 resourceMetadata dclmetadata.Resource, isCollectionItemSchema bool) (interface{}, error) {
166 if val == nil {
167 return nil, nil
168 }
169 switch schema.Type {
170 case "object":
171 obj, ok := val.(map[string]interface{})
172 if !ok {
173 return nil, fmt.Errorf("expected the value to be map[string]interface{}, but was actually %T", val)
174 }
175 res := make(map[string]interface{})
176
177 if schema.AdditionalProperties != nil {
178 for k, v := range obj {
179 convertedVal, err := convertToKRMSpec(v, append(path, k), schema.AdditionalProperties, smLoader, resourceMetadata, true)
180 if err != nil {
181 return nil, err
182 }
183 dcl.AddToMap(k, convertedVal, res)
184 }
185 return res, nil
186 }
187 for field, fieldSchema := range schema.Properties {
188 if fieldSchema.ReadOnly && !isCollectionItemSchema {
189 continue
190 }
191 val, ok := obj[field]
192 if !ok || val == nil {
193 continue
194 }
195 isSensitive, err := extension.IsSensitiveField(fieldSchema)
196 if err != nil {
197 return nil, err
198 }
199 if !fieldSchema.ReadOnly && isSensitive {
200 convertedVal, err := convertSensitiveFieldToKRM(val)
201 if err != nil {
202 return nil, fmt.Errorf("error resolving the value for sensitive field %v: %w", field, err)
203 }
204 dcl.AddToMap(field, convertedVal, res)
205 continue
206 }
207
208
209
210
211
212
213 if !fieldSchema.ReadOnly && dcl.IsContainerField(append(path, field)) && !resourceMetadata.SupportsHierarchicalReferences {
214 convertedVal, err := convertToKRMSpec(obj[field], append(path, field), fieldSchema, smLoader, resourceMetadata, isCollectionItemSchema)
215 if err != nil {
216 return nil, fmt.Errorf("error resolving the value for DCL field %v: %w", field, err)
217 }
218 dcl.AddToMap(field, convertedVal, res)
219 continue
220 }
221 if !fieldSchema.ReadOnly && extension.IsReferenceField(fieldSchema) {
222 refField, convertedVal, err := convertReferenceFieldToKRM(append(path, field), obj[field], fieldSchema, smLoader)
223 if err != nil {
224 return nil, fmt.Errorf("error converting the value for reference field %v: %w", field, err)
225 }
226 dcl.AddToMap(refField, convertedVal, res)
227 continue
228 }
229 convertedVal, err := convertToKRMSpec(obj[field], append(path, field), fieldSchema, smLoader, resourceMetadata, isCollectionItemSchema)
230 if err != nil {
231 return nil, fmt.Errorf("error resolving the value for DCL field %v: %w", field, err)
232 }
233 dcl.AddToMap(field, convertedVal, res)
234 }
235 return res, nil
236 case "array":
237 items, ok := val.([]interface{})
238 if !ok {
239 return nil, fmt.Errorf("expected the value to be array but was actually %T", val)
240 }
241 if len(items) == 0 {
242 return nil, nil
243 }
244 res := make([]interface{}, 0)
245 for _, item := range items {
246 processedItem, err := convertToKRMSpec(item, path, schema.Items, smLoader, resourceMetadata, true)
247 if err != nil {
248 return nil, fmt.Errorf("error converting list item: %v", err)
249 }
250 res = append(res, processedItem)
251 }
252 return res, nil
253 case "string", "boolean", "number", "integer":
254 return val, nil
255 default:
256 return nil, fmt.Errorf("unknown schema type %v", schema.Type)
257 }
258 }
259
260 func getStatusFieldsWithValuePopulated(path string, val interface{}, schema *openapi.Schema, paths []string) ([]string, error) {
261 if val == nil {
262 return paths, nil
263 }
264 if schema.ReadOnly {
265 paths = append(paths, path)
266 return paths, nil
267 }
268 if schema.Type == "object" {
269 obj, ok := val.(map[string]interface{})
270 if !ok {
271 return nil, fmt.Errorf("expected the value to be map[string]interface{} but was actually %T", val)
272 }
273 if len(obj) == 0 {
274 return paths, nil
275 }
276 if schema.AdditionalProperties != nil {
277 return getStatusFieldsWithValuePopulated(path, obj, schema.AdditionalProperties, paths)
278 }
279 var err error
280 for k, v := range obj {
281 fieldSchema, ok := schema.Properties[k]
282 if !ok {
283 continue
284 }
285 qualifiedName := k
286 if path != "" {
287 qualifiedName = path + "." + k
288 }
289 paths, err = getStatusFieldsWithValuePopulated(qualifiedName, v, fieldSchema, paths)
290 if err != nil {
291 return nil, fmt.Errorf("error getting status fields with value from %v: %w", qualifiedName, err)
292 }
293 }
294 return paths, nil
295 }
296 return paths, nil
297 }
298
299 func convertToKRMStatus(obj map[string]interface{}, schema *openapi.Schema) (map[string]interface{}, error) {
300 paths := make([]string, 0)
301 paths, err := getStatusFieldsWithValuePopulated("", obj, schema, paths)
302 if err != nil {
303 return nil, fmt.Errorf("error getting status fields from DCL object: %w", err)
304 }
305 status := make(map[string]interface{})
306 for _, path := range paths {
307 val, found, err := unstructured.NestedFieldCopy(obj, strings.Split(path, ".")...)
308 if err != nil {
309 return nil, fmt.Errorf("error copying the value for status field %v: %w", path, err)
310 }
311 if !found {
312 return nil, fmt.Errorf("couldn't find the value for status field %v", path)
313 }
314 splitPath := strings.Split(path, ".")
315 splitPath = renameStatusFieldIfCollidesWithReservedName(splitPath)
316 if err := unstructured.SetNestedField(status, val, splitPath...); err != nil {
317 return nil, fmt.Errorf("error setting the value for status field %v: %w", path, err)
318 }
319 }
320 return status, nil
321 }
322
323 func convertToDCL(val interface{}, path []string, schema *openapi.Schema,
324 smLoader dclmetadata.ServiceMetadataLoader, isCollectionItemSchema bool) (interface{}, error) {
325 if val == nil {
326 return nil, nil
327 }
328 switch schema.Type {
329 case "object":
330 obj, ok := val.(map[string]interface{})
331 if !ok {
332 return nil, fmt.Errorf("expected the value to be map[string]interface{} but was actually %T", val)
333 }
334
335 if schema.AdditionalProperties != nil {
336 res := make(map[string]interface{})
337 for k, v := range obj {
338 dclVal, err := convertToDCL(v, append(path, k), schema.AdditionalProperties, smLoader, true)
339 if err != nil {
340 return nil, fmt.Errorf("error converting item with AdditionalProperties schema: %w", err)
341 }
342 dcl.AddToMap(k, dclVal, res)
343 }
344 return res, nil
345 }
346 res := make(map[string]interface{})
347 for field, fieldSchema := range schema.Properties {
348
349
350
351
352
353
354 if fieldSchema.ReadOnly && !isCollectionItemSchema {
355 continue
356 }
357
358
359
360
361
362
363
364 isSensitive, err := extension.IsSensitiveField(fieldSchema)
365 if err != nil {
366 return nil, err
367 }
368 if !fieldSchema.ReadOnly && isSensitive {
369 convertedVal, err := convertSensitiveFieldToDCL(append(path, field), obj)
370 if err != nil {
371 return nil, fmt.Errorf("error resolving the value for sensitive field %v: %w", field, err)
372 }
373 dcl.AddToMap(field, convertedVal, res)
374 continue
375 }
376 if !fieldSchema.ReadOnly && extension.IsReferenceField(fieldSchema) {
377 convertedVal, err := convertReferenceFieldToDCL(append(path, field), obj, fieldSchema, smLoader)
378 if err != nil {
379 return nil, fmt.Errorf("error resolving the value for reference field %v: %w", field, err)
380 }
381 dcl.AddToMap(field, convertedVal, res)
382 continue
383 }
384 convertedVal, err := convertToDCL(obj[field], append(path, field), fieldSchema, smLoader, isCollectionItemSchema)
385 if err != nil {
386 return nil, fmt.Errorf("error resolving the value for DCL field %v: %w", field, err)
387 }
388 dcl.AddToMap(field, convertedVal, res)
389 }
390 return res, nil
391 case "array":
392 items, ok := val.([]interface{})
393 if !ok {
394 return nil, fmt.Errorf("expected the value to be []interface{} but was actually %T", val)
395 }
396 res := make([]interface{}, 0)
397 for _, item := range items {
398 processedItem, err := convertToDCL(item, path, schema.Items, smLoader, true)
399 if err != nil {
400 return nil, fmt.Errorf("error converting the item: %v", err)
401 }
402 res = append(res, processedItem)
403 }
404 return res, nil
405 case "integer":
406 return dcl.CanonicalizeIntegerValue(val)
407 case "number":
408 return dcl.CanonicalizeNumberValue(val)
409 case "string", "boolean":
410 return val, nil
411 default:
412 return nil, fmt.Errorf("unknown schema type %v", schema.Type)
413 }
414 }
415
416 func convertSensitiveFieldToDCL(path []string, obj map[string]interface{}) (interface{}, error) {
417 field := pathslice.Base(path)
418 if obj[field] == nil {
419 return nil, nil
420 }
421 raw := obj[field]
422 secretRef, ok := raw.(map[string]interface{})
423 if !ok {
424 return nil, fmt.Errorf("expected the value to be map[string]interface{} for field %v but was actually %T", field, raw)
425 }
426 return secretRef["value"], nil
427 }
428
429 func convertSensitiveFieldToKRM(val interface{}) (interface{}, error) {
430 if val == nil {
431 return nil, nil
432 }
433 sensitiveFieldStruct := make(map[string]interface{})
434 sensitiveFieldStruct["value"] = val
435 return sensitiveFieldStruct, nil
436 }
437
438 func convertReferenceFieldToDCL(path []string, obj map[string]interface{}, schema *openapi.Schema,
439 smLoader dclmetadata.ServiceMetadataLoader) (interface{}, error) {
440 if dcl.IsMultiTypeParentReferenceField(path) {
441 return convertMultiTypeParentReferenceFieldToDCL(obj, schema, smLoader)
442 }
443 if schema.Type == "array" {
444 return convertListOfReferencesFieldToDCL(path, obj, schema)
445 }
446 return convertRegularReferenceFieldToDCL(path, obj, schema)
447 }
448
449 func convertMultiTypeParentReferenceFieldToDCL(obj map[string]interface{}, schema *openapi.Schema,
450 smLoader dclmetadata.ServiceMetadataLoader) (interface{}, error) {
451 rawVal, tc, err := dcl.GetHierarchicalRefFromConfigForMultiParentResource(obj, schema, smLoader)
452 if err != nil {
453 return nil, fmt.Errorf("error getting hierarchical reference from config for multi-parent resource: %w", err)
454 }
455 if rawVal == nil {
456 return nil, fmt.Errorf("no hierarchical reference found for multi-parent resource")
457 }
458 refField := tc.Key
459 refObj, ok := rawVal.(map[string]interface{})
460 if !ok {
461 return nil, fmt.Errorf("expected the value to be map[string]interface{} for reference field %v but was actually %T", refField, rawVal)
462 }
463
464
465
466
467 rawExternalVal, ok := refObj["external"]
468 if !ok {
469 return nil, fmt.Errorf("'external' was unexpectedly not set for reference field %v", refField)
470 }
471 externalVal, ok := rawExternalVal.(string)
472 if !ok {
473 return nil, fmt.Errorf("expected the value of 'external' to be string for reference field %v but was actually %T", refField, rawExternalVal)
474 }
475 if externalVal == "" {
476 return externalVal, nil
477 }
478 parentPrefix := dcl.ParentPrefixForKind(tc.GVK.Kind)
479 if strings.HasPrefix(externalVal, parentPrefix) {
480 return externalVal, nil
481 }
482 return fmt.Sprintf("%v%v", parentPrefix, externalVal), nil
483 }
484
485 func convertListOfReferencesFieldToDCL(path []string, obj map[string]interface{}, schema *openapi.Schema) (interface{}, error) {
486 refField, err := extension.GetReferenceFieldName(path, schema)
487 if err != nil {
488 return nil, fmt.Errorf("error getting the reference field name %w", err)
489 }
490 if obj[refField] == nil {
491 return nil, nil
492 }
493 rawVal := obj[refField]
494 items, ok := rawVal.([]interface{})
495 if !ok {
496 return nil, fmt.Errorf("expected the value to be []interface{} for reference field %v but was actually %T", refField, rawVal)
497 }
498 res := make([]interface{}, 0)
499 for _, item := range items {
500 refObj, ok := item.(map[string]interface{})
501 if !ok {
502 return nil, fmt.Errorf("expected the value for the reference to be map[string]interface{}, but was actually %T", item)
503 }
504 refVal, err := resolveReferenceValue(refObj, schema.Items)
505 if err != nil {
506 return nil, err
507 }
508 res = append(res, refVal)
509 }
510 return res, nil
511 }
512
513 func convertRegularReferenceFieldToDCL(path []string, obj map[string]interface{}, schema *openapi.Schema) (interface{}, error) {
514 refField, err := extension.GetReferenceFieldName(path, schema)
515 if err != nil {
516 return nil, fmt.Errorf("error getting the reference field name %w", err)
517 }
518 if obj[refField] == nil {
519 return nil, nil
520 }
521 rawVal := obj[refField]
522 refObj, ok := rawVal.(map[string]interface{})
523 if !ok {
524 return nil, fmt.Errorf("expected the value to be map[string]interface{} for reference field %v but was actually %T", refField, rawVal)
525 }
526 return resolveReferenceValue(refObj, schema)
527 }
528
529 func convertReferenceFieldToKRM(path []string, val interface{}, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader) (string, interface{}, error) {
530 if dcl.IsMultiTypeParentReferenceField(path) {
531 return convertMultiTypeParentReferenceFieldToKRM(path, val, schema, smLoader)
532 }
533 if schema.Type == "array" {
534 return convertListOfReferencesFieldToKRM(path, val, schema)
535 }
536 return convertRegularReferenceToKRM(path, val, schema)
537 }
538
539 func convertMultiTypeParentReferenceFieldToKRM(path []string, val interface{}, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader) (string, interface{}, error) {
540 field := pathslice.Base(path)
541 tcs, err := dcl.GetReferenceTypeConfigs(schema, smLoader)
542 if err != nil {
543 return "", nil, fmt.Errorf("error getting reference type configs for DCL field '%v': %w", field, err)
544 }
545 v, ok := val.(string)
546 if !ok {
547 return "", nil, fmt.Errorf("expected the value to be string for DCL field '%v' but was actually %T", field, val)
548 }
549 if v == "" {
550 return "", nil, fmt.Errorf("value of DCL field '%v' is unexpectedly an empty string", field)
551 }
552 for _, tc := range tcs {
553
554
555 if strings.HasPrefix(v, dcl.ParentPrefixForKind(tc.GVK.Kind)) {
556 convertedVal := map[string]interface{}{
557 "external": v,
558 }
559 return tc.Key, convertedVal, nil
560 }
561 }
562 return "", nil, fmt.Errorf("value for DCL field %v could not be recognized as a valid parent: %v", field, v)
563 }
564
565 func convertListOfReferencesFieldToKRM(path []string, val interface{}, schema *openapi.Schema) (string, interface{}, error) {
566 field := pathslice.Base(path)
567 refField, err := extension.GetReferenceFieldName(path, schema)
568 if err != nil {
569 return "", nil, fmt.Errorf("error getting the reference field name %w", err)
570 }
571 items, ok := val.([]interface{})
572 if !ok {
573 return "", nil, fmt.Errorf("expected the value to be []interface{} for reference field %v but was actually %T", refField, val)
574 }
575 res := make([]interface{}, 0)
576 for _, item := range items {
577 convertedVal, err := convertReferenceValueToKRM(item, schema.Items)
578 if err != nil {
579 return "", nil, err
580 }
581 res = append(res, convertedVal)
582 }
583 return field, res, nil
584 }
585
586 func convertRegularReferenceToKRM(path []string, val interface{}, schema *openapi.Schema) (string, interface{}, error) {
587 field := pathslice.Base(path)
588 refField, err := extension.GetReferenceFieldName(path, schema)
589 if err != nil {
590 return "", nil, fmt.Errorf("error getting the reference field name %w", err)
591 }
592 convertedVal, err := convertReferenceValueToKRM(val, schema)
593 if err != nil {
594 return "", nil, fmt.Errorf("error converting reference value %v to KRM format for field %v: %w", val, field, err)
595 }
596 return refField, convertedVal, nil
597 }
598
599 func convertReferenceValueToKRM(val interface{}, schema *openapi.Schema) (interface{}, error) {
600 refConfigs, err := getDCLReferenceExtension(schema)
601 if err != nil {
602 return nil, fmt.Errorf("error getting DCL reference extension for reference value %v: %w", val, err)
603 }
604 if len(refConfigs) >= 1 {
605 res := make(map[string]interface{})
606 res["external"] = val
607 return res, nil
608 }
609 return nil, fmt.Errorf("getting empty resource types list for reference value")
610 }
611
612 func getDCLReferenceExtension(schema *openapi.Schema) ([]interface{}, error) {
613 raw, ok := schema.Extension["x-dcl-references"]
614 if !ok {
615 return nil, fmt.Errorf("'x-dcl-references' extension is not defined")
616 }
617 refConfigs, ok := raw.([]interface{})
618 if !ok {
619 return nil, fmt.Errorf("wrong type for 'x-dcl-references' extension: %T, expect to have []interface{}", raw)
620 }
621 return refConfigs, nil
622 }
623
624 func resolveReferenceValue(obj map[string]interface{}, schema *openapi.Schema) (interface{}, error) {
625 if obj == nil {
626 return nil, nil
627 }
628 refConfigs, err := getDCLReferenceExtension(schema)
629 if err != nil {
630 return nil, fmt.Errorf("error getting DCL reference extension: %w", err)
631 }
632 if len(refConfigs) >= 1 {
633 return obj["external"], nil
634 }
635 return nil, fmt.Errorf("couldn't resolve the reference value from %v", obj)
636 }
637
638 func convertToDCLContainerField(obj *unstructured.Unstructured, r *dclunstruct.Resource, schema *openapi.Schema) error {
639 container, found, err := getContainerFieldName(schema)
640 if err != nil {
641 return fmt.Errorf("error getting the contianer field name %w", err)
642 }
643 if !found {
644 return nil
645 }
646 annotations := obj.GetAnnotations()
647 key := fmt.Sprintf("%s/%s-id", k8s.CNRMGroup, container)
648 containerID := annotations[key]
649 if containerID == "" {
650 return fmt.Errorf("couldn't resolve the value for container field %s", container)
651 }
652 r.Object[container] = containerID
653 return nil
654 }
655
656 func liftDCLContainerField(obj *unstructured.Unstructured, schema *openapi.Schema) error {
657 container, found, err := getContainerFieldName(schema)
658 if err != nil {
659 return fmt.Errorf("error getting the contianer field name %w", err)
660 }
661 if !found {
662 return nil
663 }
664 val, ok, err := unstructured.NestedString(obj.Object, "spec", container)
665 if err != nil || !ok {
666 return fmt.Errorf("couldn't get the value for container field %s: %w", container, err)
667 }
668 annotations := obj.GetAnnotations()
669 if annotations == nil {
670 annotations = make(map[string]string)
671 }
672 key := fmt.Sprintf("%s/%s-id", k8s.CNRMGroup, container)
673 annotations[key] = val
674 obj.SetAnnotations(annotations)
675 unstructured.RemoveNestedField(obj.Object, "spec", container)
676 return nil
677 }
678
679 func getContainerFieldName(schema *openapi.Schema) (string, bool, error) {
680 raw, ok := schema.Extension["x-dcl-parent-container"]
681 if !ok {
682 return "", false, nil
683 }
684
685 container, ok := raw.(string)
686 if !ok {
687 return "", false, fmt.Errorf("wrong type for 'x-dcl-parent-container' extension: %T, expect to have string type", raw)
688 }
689 return container, true, nil
690 }
691
692 func liftDCLLabelsField(obj *unstructured.Unstructured, dclSchema *openapi.Schema) error {
693 labelsField, _, found, err := extension.GetLabelsFieldSchema(dclSchema)
694 if err != nil {
695 return fmt.Errorf("error getting DCL labels field : '%v'", err)
696 }
697 if !found {
698 return nil
699 }
700
701 valMap, found, err := unstructured.NestedMap(obj.Object, "spec", labelsField)
702 if err != nil {
703 return fmt.Errorf("error getting labels %w", err)
704 }
705 if !found {
706 return nil
707 }
708 labels := make(map[string]string)
709 for k, v := range valMap {
710 labels[k] = v.(string)
711 }
712 obj.SetLabels(labels)
713 unstructured.RemoveNestedField(obj.Object, "spec", labelsField)
714 return nil
715 }
716
717
718
719 func convertToKRMResourceIDField(obj *unstructured.Unstructured, schema *openapi.Schema) error {
720
721
722 s, found := extension.GetNameFieldSchema(schema)
723 if !found {
724 return nil
725 }
726
727
728
729 if s.ReadOnly {
730 return nil
731 }
732 val, found, err := unstructured.NestedString(obj.Object, "spec", "name")
733 if err != nil {
734 return fmt.Errorf("error getting the value of 'name' field: %w", err)
735 }
736 if !found {
737 return fmt.Errorf("'name' field is not found")
738 }
739 if err := unstructured.SetNestedField(obj.Object, val, "spec", "resourceID"); err != nil {
740 return fmt.Errorf("error setting resourceID field: %w", err)
741 }
742 unstructured.RemoveNestedField(obj.Object, "spec", "name")
743 return nil
744 }
745
746
747 func convertToDCLNameField(obj *unstructured.Unstructured, r *dclunstruct.Resource, schema *openapi.Schema) error {
748
749
750 s, found := extension.GetNameFieldSchema(schema)
751 if !found {
752 return nil
753 }
754
755
756
757 if s.ReadOnly {
758 return nil
759 }
760
761 isServerGeneratedID, err := extension.IsResourceIDFieldServerGenerated(s)
762 if err != nil {
763 return fmt.Errorf("error parsing 'name' field schema: %w", err)
764 }
765
766 val, found, err := unstructured.NestedString(obj.UnstructuredContent(), "spec", "resourceID")
767 if err != nil {
768 return fmt.Errorf("error getting the value of %s: %w", k8s.ResourceIDFieldPath, err)
769 }
770 if !found {
771
772
773 if isServerGeneratedID {
774 return nil
775 }
776
777
778 val = obj.GetName()
779 }
780 if val == "" {
781 return fmt.Errorf("the resolved value for 'name' field is invalid: '' (empty string)")
782 }
783 r.Object["name"] = val
784 return nil
785 }
786
787 func convertToDCLLabelsField(obj *unstructured.Unstructured, r *dclunstruct.Resource, schema *openapi.Schema) error {
788 labelsField, _, _, err := extension.GetLabelsFieldSchema(schema)
789 if err != nil {
790 return fmt.Errorf("error getting DCL labels field: '%v'", err)
791 }
792
793 if _, ok := schema.Properties[labelsField]; !ok {
794 return nil
795 }
796 labels := label.NewGCPLabelsFromK8SLabels(obj.GetLabels(), label.GetDefaultLabels())
797 if len(labels) != 0 {
798 r.Object[labelsField] = labels
799 }
800 return nil
801 }
802
803 func renameStatusFieldIfCollidesWithReservedName(path []string) []string {
804 reservedNames := k8s.ReservedStatusFieldNames()
805 if _, ok := reservedNames[path[0]]; ok {
806 path[0] = k8s.RenameStatusFieldWithReservedName(path[0])
807 }
808 return path
809 }
810
View as plain text