1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package kcclite
16
17 import (
18 "fmt"
19 "strings"
20
21 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
22 corekccv1alpha1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl"
24 dclextension "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/extension"
25 dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
26 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/schema/dclschemaloader"
27 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/deepcopy"
28 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
29 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"
30 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util"
31 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/pathslice"
32 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/typeutil"
33
34 "github.com/nasa9084/go-openapi"
35 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
36 "sigs.k8s.io/controller-runtime/pkg/client"
37 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
38 )
39
40
41
42
43
44 func ToKCCLite(resource *dcl.Resource, smLoader dclmetadata.ServiceMetadataLoader,
45 schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader,
46 kubeClient client.Client) (*unstructured.Unstructured, error) {
47 kccLite, _, err := convertToKCCLite(resource, smLoader, schemaLoader, serviceMappingLoader, kubeClient, true)
48 return kccLite, err
49 }
50
51 func ToKCCLiteBestEffort(resource *dcl.Resource, smLoader dclmetadata.ServiceMetadataLoader,
52 schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader,
53 kubeClient client.Client) (*unstructured.Unstructured, error) {
54 kccLite, _, err := convertToKCCLite(resource, smLoader, schemaLoader, serviceMappingLoader, kubeClient, false)
55 return kccLite, err
56 }
57
58 func ToKCCLiteAndSecretVersions(resource *dcl.Resource, smLoader dclmetadata.ServiceMetadataLoader,
59 schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader,
60 kubeClient client.Client) (kccLite *unstructured.Unstructured, secretVersions map[string]string, err error) {
61 return convertToKCCLite(resource, smLoader, schemaLoader, serviceMappingLoader, kubeClient, true)
62 }
63
64 func convertToKCCLite(resource *dcl.Resource, smLoader dclmetadata.ServiceMetadataLoader,
65 schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader,
66 kubeClient client.Client, mustResolveAllFields bool) (kccLite *unstructured.Unstructured, secretVersions map[string]string, err error) {
67 lite, err := resource.MarshalAsUnstructured()
68 if err != nil {
69 return nil, nil, err
70 }
71 config, found, err := unstructured.NestedFieldNoCopy(lite.Object, "spec")
72 if err != nil {
73 return nil, nil, err
74 }
75 if !found || config == nil {
76 return lite, nil, nil
77 }
78
79 secretVersions = make(map[string]string)
80 convertedSpec, err := convertConfig(config.(map[string]interface{}), []string{}, resource.Schema, smLoader, schemaLoader, serviceMappingLoader, resource.GetNamespace(), kubeClient, mustResolveAllFields, secretVersions)
81
82 if err != nil {
83 return nil, nil, err
84 }
85 if err := unstructured.SetNestedMap(lite.Object, convertedSpec, "spec"); err != nil {
86 return nil, nil, err
87 }
88 return lite, secretVersions, nil
89 }
90
91 func convertConfig(config map[string]interface{}, path []string, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader, schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader, namespace string, kubeClient client.Client, mustResolveAllFields bool, secretVersions map[string]string) (map[string]interface{}, error) {
92 if len(config) == 0 {
93 return config, nil
94 }
95 if schema.Type != "object" {
96 return nil, fmt.Errorf("expect the schame type to be 'object', but got %v", schema.Type)
97 }
98 for f, s := range schema.Properties {
99 if dclextension.IsReferenceField(s) {
100 if err := handleReferenceField(append(path, f), config, s, smLoader, schemaLoader, serviceMappingLoader, kubeClient, namespace, mustResolveAllFields); err != nil {
101 return nil, fmt.Errorf("error resolving reference field %v: %w", f, err)
102 }
103 continue
104 }
105 if config[f] != nil {
106 convertedVal, err := convertVal(config[f], append(path, f), s, smLoader, schemaLoader, serviceMappingLoader, namespace, kubeClient, mustResolveAllFields, secretVersions)
107 if err != nil {
108 return nil, err
109 }
110 delete(config, f)
111
112
113
114 if convertedVal != nil {
115 dcl.AddToMap(f, convertedVal, config)
116 }
117 }
118 }
119 return config, nil
120 }
121
122 func convertVal(val interface{}, path []string, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader, schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader, namespace string, kubeClient client.Client, mustResolveAllFields bool, secretVersions map[string]string) (interface{}, error) {
123 switch schema.Type {
124 case "object":
125 obj, ok := val.(map[string]interface{})
126 if !ok {
127 return nil, fmt.Errorf("expected the value to be map[string]interface{} but was actually %T", val)
128 }
129 if schema.AdditionalProperties != nil {
130 if typeutil.IsPrimitiveType(schema.AdditionalProperties.Type) {
131 return val, nil
132 }
133 if schema.AdditionalProperties.Type == "object" {
134 res := make(map[string]interface{})
135 for k, v := range obj {
136 convertedVal, err := convertVal(v, append(path, k), schema.AdditionalProperties, smLoader, schemaLoader, serviceMappingLoader, namespace, kubeClient, mustResolveAllFields, secretVersions)
137 if err != nil {
138 return nil, fmt.Errorf("error converting the object value for key %v: %w", k, err)
139 }
140 res[k] = convertedVal
141 }
142 return res, nil
143 }
144 return nil, fmt.Errorf("not supported type for AdditionalProperties %v", schema.AdditionalProperties.Type)
145 }
146 return convertConfig(obj, path, schema, smLoader, schemaLoader, serviceMappingLoader, namespace, kubeClient, mustResolveAllFields, secretVersions)
147 case "array":
148 if typeutil.IsPrimitiveType(schema.Items.Type) {
149 return val, nil
150 }
151 items, ok := val.([]interface{})
152 if !ok {
153 return nil, fmt.Errorf("expected the value to be []interface{} but was actually %T", val)
154 }
155 res := make([]interface{}, 0)
156 for _, item := range items {
157 processedItem, err := convertVal(item, path, schema.Items, smLoader, schemaLoader, serviceMappingLoader, namespace, kubeClient, mustResolveAllFields, secretVersions)
158 if err != nil {
159 return nil, fmt.Errorf("error converting list item: %w", err)
160 }
161 res = append(res, processedItem)
162 }
163 return res, nil
164 case "string":
165 if ok, _ := dclextension.IsSensitiveField(schema); ok {
166 field := corekccv1alpha1.SensitiveField{}
167 if err := util.Marshal(val, &field); err != nil {
168 return nil, fmt.Errorf("error parsing %v onto a SensitiveField struct: %w", val, err)
169 }
170
171 if field.Value != nil {
172 return map[string]interface{}{"value": *field.Value}, nil
173 }
174
175 secretKeyRef := field.ValueFrom.SecretKeyRef
176 secretVal, secretVer, err := k8s.GetSecretVal(secretKeyRef, namespace, kubeClient)
177 if err != nil {
178 if mustResolveAllFields {
179 return nil, err
180 }
181
182
183
184 return nil, nil
185 }
186 secretVersions[secretKeyRef.Name] = secretVer
187 return map[string]interface{}{"value": secretVal}, nil
188 }
189 return val, nil
190 case "boolean", "number", "integer":
191 return val, nil
192 default:
193 return nil, fmt.Errorf("unknown schema type %v", schema.Type)
194 }
195 }
196
197 func handleReferenceField(path []string, config map[string]interface{}, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader,
198 schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader,
199 kubeClient client.Client, namespace string, mustResolveAllFields bool) error {
200 if dcl.IsMultiTypeParentReferenceField(path) {
201 return handleMultiTypeParentReferenceField(config, schema, smLoader, schemaLoader, kubeClient, namespace, mustResolveAllFields)
202 }
203 if schema.Type == "array" {
204 return handleListOfReferencesField(path, config, schema, smLoader, schemaLoader, serviceMappingLoader, kubeClient, namespace, mustResolveAllFields)
205 }
206 return handleRegularReferenceField(path, config, schema, smLoader, schemaLoader, serviceMappingLoader, kubeClient, namespace, mustResolveAllFields)
207 }
208
209 func handleMultiTypeParentReferenceField(config map[string]interface{}, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader,
210 schemaLoader dclschemaloader.DCLSchemaLoader, kubeClient client.Client, namespace string, mustResolveAllFields bool) error {
211 rawVal, tc, err := dcl.GetHierarchicalRefFromConfigForMultiParentResource(config, schema, smLoader)
212 if err != nil {
213 return fmt.Errorf("error getting hierarchical reference from config for multi-parent resource: %w", err)
214 }
215 if rawVal == nil {
216 return nil
217 }
218 refField := tc.Key
219 refObj, ok := rawVal.(map[string]interface{})
220 if !ok {
221 return fmt.Errorf("expected the value to be map[string]interface{} for reference field %v but was actually %T", refField, rawVal)
222 }
223 val, err := resolveHierarchicalReferenceForMultiParentResource(refObj, tc, namespace, kubeClient, smLoader, schemaLoader)
224 if err != nil {
225 if mustResolveAllFields {
226 return err
227 }
228 delete(config, refField)
229 return nil
230 }
231 delete(config, refField)
232 dcl.AddToMap(refField, val, config)
233 return nil
234 }
235
236 func handleListOfReferencesField(path []string, config map[string]interface{}, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader,
237 schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader,
238 kubeClient client.Client, namespace string, mustResolveAllFields bool) error {
239 refField, err := dclextension.GetReferenceFieldName(path, schema)
240 if err != nil {
241 return fmt.Errorf("error getting the reference field name %w", err)
242 }
243 if config[refField] == nil {
244 return nil
245 }
246 rawVal := config[refField]
247 items, ok := rawVal.([]interface{})
248 if !ok {
249 return fmt.Errorf("expected the value to be []interface{} for reference field %v but was actually %T", refField, rawVal)
250 }
251 res := make([]interface{}, 0)
252 for _, item := range items {
253 refObj, ok := item.(map[string]interface{})
254 if !ok {
255 return fmt.Errorf("expected the value for item reference to be map[string]interface{}, but was actually %T", item)
256 }
257 refVal, err := resolveResourceReference(refObj, schema.Items, smLoader, schemaLoader, serviceMappingLoader, kubeClient, namespace)
258 if err != nil {
259 if mustResolveAllFields {
260 return err
261 }
262 continue
263 }
264 res = append(res, refVal)
265 }
266 delete(config, refField)
267 if len(res) != 0 {
268 config[refField] = res
269 }
270 return nil
271 }
272
273 func handleRegularReferenceField(path []string, config map[string]interface{}, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader,
274 schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader,
275 kubeClient client.Client, namespace string, mustResolveAllFields bool) error {
276 refField, err := dclextension.GetReferenceFieldName(path, schema)
277 if err != nil {
278 return fmt.Errorf("error getting the reference field name %w", err)
279 }
280 if config[refField] == nil {
281 return nil
282 }
283 rawVal := config[refField]
284 refObj, ok := rawVal.(map[string]interface{})
285 if !ok {
286 return fmt.Errorf("expected the value to be map[string]interface{} for reference field %v but was actually %T", refField, rawVal)
287 }
288 val, err := resolveResourceReference(refObj, schema, smLoader, schemaLoader, serviceMappingLoader, kubeClient, namespace)
289 if err != nil {
290 if mustResolveAllFields {
291 return err
292 }
293 delete(config, refField)
294 return nil
295 }
296 delete(config, refField)
297 dcl.AddToMap(refField, val, config)
298 return nil
299 }
300
301 func resolveHierarchicalReferenceForMultiParentResource(resourceRefValRaw map[string]interface{}, tc *corekccv1alpha1.TypeConfig, ns string,
302 kubeClient client.Client, smLoader dclmetadata.ServiceMetadataLoader, schemaLoader dclschemaloader.DCLSchemaLoader) (map[string]interface{}, error) {
303 val, err := resolveReferenceObject(resourceRefValRaw, tc, ns, kubeClient)
304 if err != nil {
305 return nil, err
306 }
307
308
309
310
311
312
313
314
315
316
317
318 return map[string]interface{}{
319 "external": val,
320 }, nil
321 }
322
323 func resolveResourceReference(resourceRefValRaw map[string]interface{}, schema *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader,
324 schemaLoader dclschemaloader.DCLSchemaLoader, serviceMappingLoader *servicemappingloader.ServiceMappingLoader,
325 kubeClient client.Client, ns string) (map[string]interface{}, error) {
326 tcs, err := dcl.GetReferenceTypeConfigs(schema, smLoader)
327 if err != nil {
328 return nil, err
329 }
330
331
332
333 if _, ok := resourceRefValRaw["external"]; ok {
334 return resourceRefValRaw, nil
335 }
336
337 tc, refResource, err := getMultiTypeReferencedResource(resourceRefValRaw, tcs, ns, kubeClient)
338 if err != nil {
339 return nil, err
340 }
341 val, err := resolveTargetFieldValue(refResource, tc)
342 if err != nil {
343 return nil, fmt.Errorf("error resolving target field value for referenced resource %v with GroupVersionKind %v: %w",
344 k8s.GetNamespacedName(refResource), refResource.GroupVersionKind(), err)
345 }
346
347
348 if tc.TargetField == "name" {
349 s, err := dclschemaloader.GetDCLSchemaForGVK(tc.GVK, smLoader, schemaLoader)
350 if err != nil {
351 return nil, fmt.Errorf("error getting DCL schema for referenced GroupVersionKind %v: %w", tc.GVK, err)
352 }
353 template, err := dclextension.GetNameValueTemplate(s)
354 if err != nil {
355 return nil, fmt.Errorf("error getting name value template for referenced GroupVersionKind %v: %w", tc.GVK, err)
356 }
357 canonicalizedVal, err := CanonicalizeReferencedResourceName(val, template, refResource, smLoader, schemaLoader, serviceMappingLoader, kubeClient)
358 if err != nil {
359 return nil, fmt.Errorf("error canonicalizing name of referenced resource %v with GroupVersionKind %v: %w",
360 k8s.GetNamespacedName(refResource), refResource.GroupVersionKind(), err)
361 }
362 return map[string]interface{}{
363 "external": canonicalizedVal,
364 }, nil
365 }
366 return map[string]interface{}{
367 "external": val,
368 }, nil
369 }
370
371 func resolveReferenceObject(resourceRefValRaw map[string]interface{}, tc *corekccv1alpha1.TypeConfig,
372 ns string, kubeClient client.Client) (string, error) {
373 if rawVal, ok := resourceRefValRaw["external"]; ok {
374 val, ok := rawVal.(string)
375 if !ok {
376 return "", fmt.Errorf("expected the value of 'external' in the resource reference object to be string, but was actually %T", rawVal)
377 }
378 return val, nil
379 }
380 refResource, err := getReferencedResource(resourceRefValRaw, tc, ns, kubeClient)
381 if err != nil {
382 return "", err
383 }
384 val, err := resolveTargetFieldValue(refResource, tc)
385 if err != nil {
386 return "", fmt.Errorf("error resolving target field value for referenced resource %v with GroupVersionKind %v: %w",
387 k8s.GetNamespacedName(refResource), refResource.GroupVersionKind(), err)
388 }
389 return val, nil
390 }
391
392 func getMultiTypeReferencedResource(resourceRefValRaw map[string]interface{}, tcs []corekccv1alpha1.TypeConfig,
393 ns string, kubeClient client.Client) (*corekccv1alpha1.TypeConfig, *k8s.Resource, error) {
394
395 if len(tcs) == 0 {
396 return nil, nil, fmt.Errorf("error resolving resource reference, no resource type information found")
397 }
398
399 if rawVal, ok := resourceRefValRaw["kind"]; ok {
400
401 kind, ok := rawVal.(string)
402 if !ok {
403 return nil, nil, fmt.Errorf("expected the value of 'kind' in the resource reference object to be string, but was actually %T", rawVal)
404 }
405 if len(tcs) == 1 {
406
407 return nil, nil, fmt.Errorf("'kind' is found in the single-type resource reference")
408 } else {
409
410 for i := range tcs {
411 tc := &tcs[i]
412 if kind == tc.GVK.Kind {
413 refResource, err := getReferencedResource(resourceRefValRaw, tc, ns, kubeClient)
414 return tc, refResource, err
415 }
416 }
417 return nil, nil, fmt.Errorf("the value of 'kind': '%v' is not supported in the resource reference", kind)
418 }
419 } else {
420
421 if len(tcs) == 1 {
422
423 tc := &tcs[0]
424 refResource, err := getReferencedResource(resourceRefValRaw, tc, ns, kubeClient)
425 return tc, refResource, err
426 } else {
427
428 return nil, nil, fmt.Errorf("'kind' is missing in the multi-type resource reference")
429 }
430 }
431 }
432
433 func getReferencedResource(resourceRefValRaw map[string]interface{}, tc *corekccv1alpha1.TypeConfig,
434 ns string, kubeClient client.Client) (*k8s.Resource, error) {
435 resourceRef := &v1alpha1.ResourceReference{}
436 if err := util.Marshal(resourceRefValRaw, resourceRef); err != nil {
437 return nil, fmt.Errorf("error marshalling raw resource reference object to resource reference struct: %w", err)
438 }
439 refResource, err := k8s.GetReferencedResourceIfReady(resourceRef, tc.GVK, ns, kubeClient)
440 if err != nil {
441 return nil, err
442 }
443 return refResource, nil
444 }
445
446
447 func resolveTargetFieldValue(refResource *k8s.Resource, typeConfig *corekccv1alpha1.TypeConfig) (string, error) {
448 if typeConfig.TargetField == "name" {
449 val, ok, err := unstructured.NestedString(refResource.Spec, k8s.ResourceIDFieldName)
450 if err != nil {
451 return "", err
452 }
453 if !ok {
454 return "", fmt.Errorf("couldn't resolve the resource Id")
455 }
456 return val, nil
457 }
458 if val, exist, _ := unstructured.NestedString(refResource.Status, strings.Split(typeConfig.TargetField, ".")...); exist {
459 return val, nil
460 }
461 if val, exist, _ := unstructured.NestedString(refResource.Spec, strings.Split(typeConfig.TargetField, ".")...); exist {
462 return val, nil
463 }
464 return "", fmt.Errorf("couldn't resolve the value for target field %v from the referenced resource %v", typeConfig.TargetField, refResource.GetNamespacedName())
465 }
466
467
468
469
470
471
472
473
474
475 func ResolveSpecAndStatus(state *unstructured.Unstructured, resource *dcl.Resource,
476 smLoader dclmetadata.ServiceMetadataLoader) (spec map[string]interface{}, status map[string]interface{}, err error) {
477 val, found := k8s.GetAnnotation(k8s.StateIntoSpecAnnotation, resource)
478 if !found || val == k8s.StateMergeIntoSpec {
479 spec, status, err = resolveMixedSpecAndLegacyStatus(state, resource, smLoader)
480 } else {
481 spec, status, err = resolveDesiredStateInSpecAndObservedStateInStatus(state, resource, smLoader)
482 }
483 if err != nil {
484 return nil, nil, err
485 }
486
487
488 normalizedSpec := make(map[string]interface{})
489 normalizedStatus := make(map[string]interface{})
490 if err := util.Marshal(&spec, &normalizedSpec); err != nil {
491 return nil, nil, fmt.Errorf("error normalizing the spec: %w", err)
492 }
493 if err := util.Marshal(&status, &normalizedStatus); err != nil {
494 return nil, nil, fmt.Errorf("error normalizing the status: %w", err)
495 }
496 return normalizedSpec, normalizedStatus, nil
497 }
498
499
500
501 func resolveMixedSpecAndLegacyStatus(state *unstructured.Unstructured, resource *dcl.Resource,
502 smLoader dclmetadata.ServiceMetadataLoader) (spec map[string]interface{}, status map[string]interface{}, err error) {
503 status, found, err := unstructured.NestedMap(state.Object, "status")
504 if err != nil {
505 return nil, nil, fmt.Errorf("error getting status from the state: %w", err)
506 }
507 if !found {
508 status = make(map[string]interface{})
509 }
510 conditions, found, err := unstructured.NestedFieldCopy(resource.Status, "conditions")
511 if err != nil {
512 return nil, nil, fmt.Errorf("error resolving conditions from resource status: %w", err)
513 }
514 if found {
515 status["conditions"] = conditions
516 }
517
518 g, found, err := unstructured.NestedFieldCopy(resource.Status, "observedGeneration")
519 if err != nil {
520 return nil, nil, fmt.Errorf("error resolving observedGeneration from resource status: %w", err)
521 }
522 if found {
523 status["observedGeneration"] = g
524 }
525
526 stateSpec, found, err := unstructured.NestedMap(state.Object, "spec")
527 if err != nil {
528 return nil, nil, fmt.Errorf("error getting spec from the state: %w", err)
529 }
530 if !found {
531 stateSpec = make(map[string]interface{})
532 }
533 mergedSpec, err := mergeSpecWithLiteState(stateSpec, resource.Spec, []string{}, resource.Schema, resource.ManagedFields, smLoader)
534 if err != nil {
535 return nil, nil, fmt.Errorf("error merging spec from the live state and the raw spec: %w", err)
536 }
537
538 if err := populateResourceIDFieldInSpec(state, resource, mergedSpec); err != nil {
539 return nil, nil, fmt.Errorf("error populating 'resourceID' field in spec: %w", err)
540 }
541
542
543 if len(mergedSpec) == 0 {
544 mergedSpec = nil
545 }
546 if len(status) == 0 {
547 status = nil
548 }
549 return mergedSpec, status, nil
550 }
551
552 func populateResourceIDFieldInSpec(state *unstructured.Unstructured, resource *dcl.Resource, spec map[string]interface{}) error {
553
554 if val, ok := resource.Spec[k8s.ResourceIDFieldName]; ok {
555 spec[k8s.ResourceIDFieldName] = val
556 return nil
557 }
558
559
560 val, found, err := unstructured.NestedString(state.Object, "spec", k8s.ResourceIDFieldName)
561 if err != nil {
562 return err
563 }
564 if found {
565 spec[k8s.ResourceIDFieldName] = val
566 }
567 return nil
568 }
569
570
571
572 func resolveDesiredStateInSpecAndObservedStateInStatus(state *unstructured.Unstructured, resource *dcl.Resource,
573 smLoader dclmetadata.ServiceMetadataLoader) (
574 spec map[string]interface{}, status map[string]interface{}, err error) {
575 if resource.Spec != nil {
576 spec = deepcopy.MapStringInterface(resource.Spec)
577 }
578 if err := populateResourceIDFieldInSpec(state, resource, spec); err != nil {
579 return nil, nil, fmt.Errorf("error populating 'resourceID' field in spec: %w", err)
580 }
581 _, status, err = resolveMixedSpecAndLegacyStatus(state, resource, smLoader)
582 if err != nil {
583 return nil, nil, err
584 }
585 return spec, status, nil
586 }
587
588 func mergeSpecWithLiteState(state map[string]interface{}, spec map[string]interface{}, path []string,
589 schema *openapi.Schema, managedFields *fieldpath.Set, smLoader dclmetadata.ServiceMetadataLoader) (map[string]interface{}, error) {
590 res := make(map[string]interface{})
591 for f, s := range schema.Properties {
592 if dclextension.IsReferenceField(s) {
593 refField, val, err := mergeReferenceField(state, spec, append(path, f), s, smLoader)
594 if err != nil {
595 return nil, err
596 }
597 if val != nil {
598 res[refField] = val
599 }
600 continue
601 }
602
603 stateVal := state[f]
604 specVal := spec[f]
605 if stateVal == nil && specVal == nil {
606 continue
607 }
608
609 if stateVal == nil {
610 res[f] = deepcopy.DeepCopy(specVal)
611 continue
612 }
613 if specVal == nil {
614 res[f] = deepcopy.DeepCopy(stateVal)
615 continue
616 }
617
618 switch s.Type {
619 case "object":
620 if s.AdditionalProperties != nil {
621 if typeutil.IsPrimitiveType(s.AdditionalProperties.Type) {
622 val, err := mergePrimitiveMap(state, spec, append(path, f), managedFields)
623 if err != nil {
624 return nil, err
625 }
626 dcl.AddToMap(f, val, res)
627 continue
628 }
629 if s.AdditionalProperties.Type == "object" {
630 val, err := mergeObjectMap(state, spec, append(path, f), s.AdditionalProperties, managedFields, smLoader)
631 if err != nil {
632 return nil, err
633 }
634 dcl.AddToMap(f, val, res)
635 continue
636 }
637 return nil, fmt.Errorf("unsupported AdditionalProperties.Type for field '%v': %v", f, s.AdditionalProperties.Type)
638 }
639
640 val, err := mergeNestedObject(state, spec, append(path, f), s, managedFields, smLoader)
641 if err != nil {
642 return nil, err
643 }
644 if val != nil {
645 res[f] = val
646 }
647 case "array":
648 if typeutil.IsPrimitiveType(s.Items.Type) {
649 listVal, ok := stateVal.([]interface{})
650 if !ok {
651 return nil, fmt.Errorf("expected the value for field '%v' to be []interface{} but was actually %T", f, stateVal)
652 }
653 if len(listVal) != 0 {
654 res[f] = deepcopy.DeepCopy(listVal)
655 }
656 continue
657 }
658 if s.Items.Type == "object" {
659 retObjList, err := mergeObjectArray(stateVal, specVal, append(path, f), s, smLoader)
660 if err != nil {
661 return nil, err
662 }
663 if len(retObjList) == 0 {
664 continue
665 }
666 res[f] = retObjList
667 continue
668 }
669 return nil, fmt.Errorf("unsupported Items.Type for the array field '%v': %v", f, s.Items.Type)
670 case "string":
671 if k8s.IsK8sManaged(f, spec, managedFields) {
672 res[f] = specVal
673 } else {
674 isSensitiveField, err := dclextension.IsSensitiveField(s)
675 if err != nil {
676 return nil, err
677 }
678 if isSensitiveField {
679
680 sensitiveVal := corekccv1alpha1.SensitiveField{}
681 if err := util.Marshal(stateVal, &sensitiveVal); err != nil {
682 return nil, err
683 }
684 res[f] = stateVal
685 } else {
686 res[f] = stateVal
687 }
688 }
689 case "boolean", "number", "integer":
690 if k8s.IsK8sManaged(f, spec, managedFields) {
691 res[f] = specVal
692 } else {
693 res[f] = stateVal
694 }
695 default:
696 return nil, fmt.Errorf("unknown schema type %v", schema.Type)
697 }
698 }
699 return res, nil
700 }
701
702 func mergeReferenceField(state map[string]interface{}, spec map[string]interface{}, path []string,
703 s *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader) (string, interface{}, error) {
704 if dcl.IsMultiTypeParentReferenceField(path) {
705 return mergeMultiTypeParentReferenceField(state, spec, s, smLoader)
706 }
707 return mergeResourceReference(state, spec, path, s)
708 }
709
710 func mergeMultiTypeParentReferenceField(state map[string]interface{}, spec map[string]interface{},
711 s *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader) (string, interface{}, error) {
712
713
714 specVal, tc, err := dcl.GetHierarchicalRefFromConfigForMultiParentResource(spec, s, smLoader)
715 if err != nil {
716 return "", nil, fmt.Errorf("error getting hierarchical reference from spec for multi-parent resource: %w", err)
717 }
718 if specVal != nil {
719 return tc.Key, specVal, nil
720 }
721
722
723 stateVal, tc, err := dcl.GetHierarchicalRefFromConfigForMultiParentResource(state, s, smLoader)
724 if err != nil {
725 return "", nil, fmt.Errorf("error getting hierarchical reference from state for multi-parent resource: %w", err)
726 }
727 if stateVal != nil {
728 return tc.Key, stateVal, nil
729 }
730
731 return "", nil, nil
732 }
733
734 func mergeResourceReference(state map[string]interface{}, spec map[string]interface{}, path []string, s *openapi.Schema) (string, interface{}, error) {
735 refField, err := dclextension.GetReferenceFieldName(path, s)
736 if err != nil {
737 return "", nil, err
738 }
739 if specVal, ok := spec[refField]; ok {
740
741
742 return refField, specVal, nil
743 } else if stateVal, ok := state[refField]; ok && stateVal != nil {
744 return refField, stateVal, nil
745 }
746 return "", nil, nil
747 }
748
749 func mergeObjectArray(stateVal, specVal interface{}, path []string, s *openapi.Schema, smLoader dclmetadata.ServiceMetadataLoader) ([]interface{}, error) {
750 field := pathslice.Base(path)
751
752 retObjList := make([]interface{}, 0)
753 specList, ok := specVal.([]interface{})
754 if !ok {
755 return nil, fmt.Errorf("expected the spec value for field '%v' to be []interface{} but was actually %T", field, specVal)
756 }
757 stateList, ok := stateVal.([]interface{})
758 if !ok {
759 return nil, fmt.Errorf("expected the state value for field '%v' to be []interface{} but was actually %T", field, stateList)
760 }
761 if len(specList) > len(stateList) {
762 return nil, fmt.Errorf("there are fewer items for field '%v' returned in state than configured in spec; state: %v, spec: %v", field, stateList, specList)
763 }
764 for idx, elem := range stateList {
765 stateObjMap, ok := elem.(map[string]interface{})
766 if !ok {
767 return nil, fmt.Errorf("expected the item value from state for field '%v' to be map[string]interface{} but was actually %T", field, elem)
768 }
769 var specObjMap map[string]interface{}
770 if idx < len(specList) {
771 specObjMap, ok = specList[idx].(map[string]interface{})
772 if !ok {
773 return nil, fmt.Errorf("expected the item value from spec for field '%v' to be map[string]interface{} but was actually %T", field, elem)
774 }
775 }
776 val, err := mergeSpecWithLiteState(stateObjMap, specObjMap, path, s.Items, nil, smLoader)
777 if err != nil {
778 return nil, err
779 }
780 if val != nil {
781 retObjList = append(retObjList, val)
782 }
783 }
784 return retObjList, nil
785 }
786
787 func mergeObjectMap(state map[string]interface{}, spec map[string]interface{}, path []string, s *openapi.Schema, managedFields *fieldpath.Set, smLoader dclmetadata.ServiceMetadataLoader) (map[string]interface{}, error) {
788 field := pathslice.Base(path)
789 var nestedManagedFields *fieldpath.Set
790 if managedFields != nil {
791 pe := fieldpath.PathElement{FieldName: &field}
792 var found bool
793 nestedManagedFields, found = managedFields.Children.Get(pe)
794 if !found {
795 nestedManagedFields = fieldpath.NewSet()
796 }
797 }
798 retObjectMap := make(map[string]interface{})
799 specObjectMap, ok := spec[field].(map[string]interface{})
800 if !ok {
801 return nil, fmt.Errorf("expected the spec value for field '%v' to be map[string]interface{} but was actually %T", field, spec[field])
802 }
803 stateObjectMap, ok := state[field].(map[string]interface{})
804 if !ok {
805 return nil, fmt.Errorf("expected the state value for field '%v' to be map[string]interface{} but was actually %T", field, state[field])
806 }
807 if len(specObjectMap) != len(stateObjectMap) {
808 return nil, fmt.Errorf("the number of items for field '%v' returned in state is not the same as configured in spec; state: %v, spec: %v", field, stateObjectMap, specObjectMap)
809 }
810
811 for k, _ := range stateObjectMap {
812 if _, ok := specObjectMap[k]; !ok {
813 return nil, fmt.Errorf("key '%v' is not configured in spec for field '%v'", k, field)
814 }
815 mergedVal, err := mergeNestedObject(stateObjectMap, specObjectMap, append(path, k), s, nestedManagedFields, smLoader)
816 if err != nil {
817 return nil, err
818 }
819 retObjectMap[k] = mergedVal
820 }
821 return retObjectMap, nil
822 }
823
824 func mergePrimitiveMap(state map[string]interface{}, spec map[string]interface{}, path []string, managedFields *fieldpath.Set) (interface{}, error) {
825 field := pathslice.Base(path)
826 if k8s.IsK8sManaged(field, spec, managedFields) {
827 return deepcopy.DeepCopy(spec[field]), nil
828 }
829 if state[field] != nil {
830 valMap, ok := state[field].(map[string]interface{})
831 if !ok {
832 return nil, fmt.Errorf("expected the value for field '%v' to be map[string]interface{} but was actually %T", field, state[field])
833 }
834 if len(valMap) != 0 {
835 return deepcopy.DeepCopy(valMap), nil
836 }
837 }
838 return nil, nil
839 }
840
841 func mergeNestedObject(state map[string]interface{}, spec map[string]interface{}, path []string, s *openapi.Schema,
842 managedFields *fieldpath.Set, smLoader dclmetadata.ServiceMetadataLoader) (map[string]interface{}, error) {
843 field := pathslice.Base(path)
844 var nestedManagedFields *fieldpath.Set
845 if managedFields != nil {
846 pe := fieldpath.PathElement{FieldName: &field}
847 var found bool
848 nestedManagedFields, found = managedFields.Children.Get(pe)
849 if !found {
850 nestedManagedFields = fieldpath.NewSet()
851 }
852 }
853 stateConfigMap, ok := state[field].(map[string]interface{})
854 if !ok {
855 return nil, fmt.Errorf("expected the state value for field '%v' to be map[string]interface{} but was actually %T", field, state[field])
856 }
857 specConfigMap, ok := spec[field].(map[string]interface{})
858 if !ok {
859 return nil, fmt.Errorf("expected the spec value for field '%v' to be map[string]interface{} but was actually %T", field, spec[field])
860 }
861 val, err := mergeSpecWithLiteState(stateConfigMap, specConfigMap, path, s, nestedManagedFields, smLoader)
862 if err != nil {
863 return nil, err
864 }
865 return val, nil
866 }
867
View as plain text