1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package krmtotf
16
17 import (
18 "fmt"
19 "strings"
20
21 corekccv1alpha1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
22 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/text"
24 tfresource "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/tf/resource"
25 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/slice"
26
27 tfschema "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
28 "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
29 apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
31 )
32
33
34
35 func resolveUnmanagedFields(spec map[string]interface{}, r *Resource, liveState *terraform.InstanceState,
36 jsonSchema *apiextensions.JSONSchemaProps) (map[string]interface{}, error) {
37 if r.ManagedFields == nil {
38
39
40
41 return spec, nil
42 }
43
44 var stateAsKRM map[string]interface{}
45 var err error
46
47 if liveState.Empty() {
48 stateAsKRM = make(map[string]interface{})
49 } else {
50 stateAsKRM, _ = GetSpecAndStatusFromState(r, liveState)
51 }
52
53 switch r.Kind {
54
55
56 case "BigtableAppProfile", "CloudBuildTrigger", "ResourceManagerPolicy":
57 if err = RemoveFieldsFromStateThatConflictWithSpec(stateAsKRM, spec, r.ResourceConfig, []string{}, r.TFResource.Schema); err != nil {
58 return nil, fmt.Errorf("error stripping fields from state that conflict with fields already in spec: %w", err)
59 }
60 }
61
62 return k8s.OverlayManagedFieldsOntoState(spec, stateAsKRM, r.ManagedFields, jsonSchema, r.ResourceConfig.HierarchicalReferences)
63 }
64
65
66
67
68
69 func RemoveFieldsFromStateThatConflictWithSpec(state map[string]interface{}, spec map[string]interface{},
70 rc corekccv1alpha1.ResourceConfig, tfPath []string, schemaMap map[string]*tfschema.Schema) error {
71 if len(state) == 0 || len(spec) == 0 {
72 return nil
73 }
74 for k, s := range schemaMap {
75 tfPath := append(tfPath, k)
76 tfField := strings.Join(tfPath, ".")
77 krmPath := text.SnakeCaseStrsToLowerCamelCaseStrs(tfPath)
78 if ok, refConfig := IsReferenceField(tfField, &rc); ok {
79 krmPath = getPathToReferenceKey(refConfig)
80 }
81
82 _, found, err := unstructured.NestedFieldNoCopy(state, krmPath...)
83 if err != nil {
84 return fmt.Errorf("error checking for existence of field with path %v in state: %w", krmPath, err)
85 }
86 if !found {
87 continue
88 }
89
90
91
92
93 conflictingTFKeys := slice.RemoveStringFromStringSlice(
94 slice.ConcatStringSlices(s.ConflictsWith, s.ExactlyOneOf),
95 strings.Join(tfPath, ".0."),
96 )
97 for _, conflictingTFKey := range conflictingTFKeys {
98 conflictingTFPath := strings.Split(conflictingTFKey, ".0.")
99 conflictingTFField := strings.Join(conflictingTFPath, ".")
100 conflictingKRMPath := text.SnakeCaseStrsToLowerCamelCaseStrs(conflictingTFPath)
101 if ok, refConfig := IsReferenceField(conflictingTFField, &rc); ok {
102 conflictingKRMPath = getPathToReferenceKey(refConfig)
103 }
104
105 _, found, err := unstructured.NestedFieldNoCopy(spec, conflictingKRMPath...)
106 if err != nil {
107 return fmt.Errorf("error checking for existence of conflicting field with path %v in spec: %w", krmPath, err)
108 }
109 if !found {
110 continue
111 }
112
113 if err := removeFieldAndAnyEmptyAncestorsFromObject(krmPath, state); err != nil {
114 return fmt.Errorf("error removing field at path %v from state: %w", krmPath, err)
115 }
116 }
117
118
119
120
121
122 if !tfresource.IsObjectField(s) {
123 continue
124 }
125 if err := RemoveFieldsFromStateThatConflictWithSpec(state, spec, rc, tfPath, s.Elem.(*tfschema.Resource).Schema); err != nil {
126 return err
127 }
128 }
129 return nil
130 }
131
132
133
134
135
136 func removeFieldAndAnyEmptyAncestorsFromObject(path []string, obj map[string]interface{}) error {
137 unstructured.RemoveNestedField(obj, path...)
138
139
140
141 if len(path) <= 1 {
142 return nil
143 }
144
145
146
147 for i := len(path) - 2; i >= 0; i-- {
148 ancestorPath := path[0 : i+1]
149 val, found, err := unstructured.NestedFieldNoCopy(obj, ancestorPath...)
150 if err != nil {
151 return fmt.Errorf("error checking for existence of field ancestor at path: %v: %w", ancestorPath, err)
152 }
153 if !found {
154 return fmt.Errorf("unexpectedly failed to find field ancestor at path %v", ancestorPath)
155 }
156 valAsObj, ok := val.(map[string]interface{})
157 if !ok {
158 return fmt.Errorf("field ancestor at path %v is unexpectedly not a map[string]interface{}", ancestorPath)
159 }
160 if len(valAsObj) > 0 {
161 return nil
162 }
163 unstructured.RemoveNestedField(obj, ancestorPath...)
164 }
165 return nil
166 }
167
View as plain text