1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package krmtotf
16
17 import (
18 "fmt"
19 "sort"
20
21 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/deepcopy"
22
23 tfschema "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
24 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
25 )
26
27 func withCustomFlatteners(config map[string]interface{}, kind string) (map[string]interface{}, error) {
28 switch kind {
29 case "ComputeInstance", "ComputeInstanceTemplate":
30 return FlattenComputeInstanceMetadata(config)
31 default:
32 return config, nil
33 }
34 }
35
36 func withCustomExpanders(state map[string]interface{}, prev *Resource, kind string) map[string]interface{} {
37 switch kind {
38 case "ComputeInstance", "ComputeInstanceTemplate":
39 return ExpandComputeInstanceMetadata(state, prev)
40 default:
41 return state
42 }
43 }
44
45 func withResourceCustomResolvers(config map[string]interface{}, liveState map[string]interface{}, kind string, r *tfschema.Resource) (map[string]interface{}, error) {
46 switch kind {
47 case "BigtableInstance":
48 return MergeClusterConfigsFromLiveStateForBigtableInstance(config, liveState, r)
49 default:
50 return config, nil
51 }
52 }
53
54
55
56
57
58
59
60 func MergeClusterConfigsFromLiveStateForBigtableInstance(config map[string]interface{}, liveState map[string]interface{}, r *tfschema.Resource) (map[string]interface{}, error) {
61 if len(liveState) == 0 || len(config) == 0 {
62 return config, nil
63 }
64 clustersRaw, found, err := unstructured.NestedFieldCopy(liveState, "cluster")
65 if err != nil {
66 return nil, err
67 }
68 if !found || clustersRaw == nil {
69 return config, nil
70 }
71 clusters, ok := clustersRaw.([]interface{})
72 if !ok {
73 return nil, fmt.Errorf("expected the value of `cluster` field in config to be []interface{}, but was actually %T", clustersRaw)
74 }
75
76 newClustersRaw, found, err := unstructured.NestedFieldCopy(config, "cluster")
77 if err != nil {
78 return nil, err
79 }
80 if !found || newClustersRaw == nil {
81 return config, nil
82 }
83 newClusters, ok := newClustersRaw.([]interface{})
84 if !ok {
85 return nil, fmt.Errorf("expected the value of `cluster` field in state to be []interface{}, but was actually %T", newClusters)
86 }
87 for _, item := range newClusters {
88 newCluster, ok := item.(map[string]interface{})
89 if !ok {
90 return nil, fmt.Errorf("expected the item value of `cluster` field to be map[string]interface{}, but was actually %T", item)
91 }
92 c, found, err := getClusterById(clusters, newCluster["cluster_id"])
93 if err != nil {
94 return nil, err
95 }
96 if !found {
97 continue
98 }
99 clusterSchema := r.Schema["cluster"].Elem.(*tfschema.Resource)
100 mergeClusterConfigWithLiveState(newCluster, c, clusterSchema)
101 }
102 config["cluster"] = newClusters
103 return config, nil
104 }
105
106 func getClusterById(clusters []interface{}, id interface{}) (map[string]interface{}, bool, error) {
107 for _, item := range clusters {
108 c, ok := item.(map[string]interface{})
109 if !ok {
110 return nil, false, fmt.Errorf("expected the item value of `cluster` field to be map[string]interface{}, but was actually %T", item)
111 }
112 if id == c["cluster_id"] {
113 return c, true, nil
114 }
115 }
116 return nil, false, nil
117 }
118
119 func mergeClusterConfigWithLiveState(config map[string]interface{}, state map[string]interface{}, clusterSchema *tfschema.Resource) {
120 for f, _ := range clusterSchema.Schema {
121 if f == "cluster_id" {
122 continue
123 }
124
125 if _, ok := config[f]; !ok {
126 if v, ok := state[f]; ok {
127 config[f] = v
128 }
129 }
130 }
131 }
132
133 func FlattenComputeInstanceMetadata(config map[string]interface{}) (map[string]interface{}, error) {
134 metadataRaw, _ := config["metadata"]
135 if metadataRaw == nil {
136 return config, nil
137 }
138 metadataList, ok := metadataRaw.([]interface{})
139 if !ok {
140 return nil, fmt.Errorf("metadata field not in expected list format")
141 }
142 config = deepcopy.MapStringInterface(config)
143 metadataMap, err := convertStructuredListToMap(metadataList)
144 if err != nil {
145 return nil, fmt.Errorf("error converting structured list to map: %w", err)
146 }
147 config["metadata"] = metadataMap
148 return config, nil
149 }
150
151 func ExpandComputeInstanceMetadata(state map[string]interface{}, prev *Resource) map[string]interface{} {
152
153
154
155 prevMetadata, _ := prev.Spec["metadata"]
156 if prevMetadata != nil {
157 state = deepcopy.MapStringInterface(state)
158 state["metadata"] = prevMetadata
159 return state
160 }
161
162
163 stateMetadataMapRaw, ok := state["metadata"]
164 if !ok || stateMetadataMapRaw == nil {
165 return state
166 }
167 stateMetadataMap, ok := stateMetadataMapRaw.(map[string]interface{})
168 if !ok {
169 panic(fmt.Errorf("metadata in state unexpectedly is not of type map[string]interface{}: %v", stateMetadataMapRaw))
170 }
171 state["metadata"] = convertMapToStructuredList(stateMetadataMap)
172 return state
173 }
174
175 func convertStructuredListToMap(l []interface{}) (map[string]interface{}, error) {
176
177
178 m := make(map[string]interface{})
179 for _, elemRaw := range l {
180 elem, ok := elemRaw.(map[string]interface{})
181 if !ok {
182 return nil, fmt.Errorf("could not process metadata list element as object")
183 }
184 key, ok := elem["key"].(string)
185 if !ok {
186 return nil, fmt.Errorf("metadata list element's key is not string: %+v", key)
187 }
188 val := elem["value"]
189 if existingVal, ok := m[key]; ok {
190 return nil, fmt.Errorf("duplicate values set for key '%v': [%v, %v]", key, existingVal, val)
191 }
192 m[key] = val
193 }
194 return m, nil
195 }
196
197 func convertMapToStructuredList(m map[string]interface{}) []interface{} {
198
199
200
201 l := make([]interface{}, 0)
202 for k, v := range m {
203 l = append(l, map[string]interface{}{
204 "key": k,
205 "value": v,
206 })
207 }
208 sort.Slice(l, func(i, j int) bool {
209 iKey, err := keyForStructuredListElement(l[i])
210 if err != nil {
211 panic(err)
212 }
213 jKey, err := keyForStructuredListElement(l[j])
214 if err != nil {
215 panic(err)
216 }
217 return iKey < jKey
218 })
219 return l
220 }
221
222 func keyForStructuredListElement(rawElem interface{}) (string, error) {
223 elem, ok := rawElem.(map[string]interface{})
224 if !ok {
225 return "", fmt.Errorf("structured list element unexpectedly does not have type map[string]interface{}: %v", rawElem)
226 }
227 rawKey, ok := elem["key"]
228 if !ok {
229 return "", fmt.Errorf("structured list element unexpectedly does not have a key: %v", rawElem)
230 }
231 key, ok := rawKey.(string)
232 if !ok {
233 return "", fmt.Errorf("structured list element's key unexpectedly does not have type string: %v", rawKey)
234 }
235 return key, nil
236 }
237
View as plain text