1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package krmtotf_test
16
17 import (
18 "encoding/json"
19 "testing"
20
21 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
22 . "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/krmtotf"
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
24
25 "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 )
28
29 var emptyObject = make(map[string]interface{})
30
31 func TestResolveGCPManagedFields(t *testing.T) {
32 tests := []struct {
33 name string
34 kind string
35 lastAppliedConfig map[string]interface{}
36 resourceExists bool
37 inputConfig map[string]interface{}
38 expectedConfig map[string]interface{}
39 }{
40 {
41 name: "ContainerCluster treats node version as GCP-managed when release channel set",
42 kind: "ContainerCluster",
43 lastAppliedConfig: map[string]interface{}{
44 "spec": map[string]interface{}{
45 "otherField": "otherValue",
46 "releaseChannel": map[string]interface{}{
47 "channel": "REGULAR",
48 },
49 },
50 },
51 resourceExists: true,
52 inputConfig: map[string]interface{}{
53 "otherField": "otherValue",
54 "releaseChannel": map[string]interface{}{
55 "channel": "REGULAR",
56 },
57 "nodeVersion": "1.14",
58 },
59 expectedConfig: map[string]interface{}{
60 "otherField": "otherValue",
61 "releaseChannel": map[string]interface{}{
62 "channel": "REGULAR",
63 },
64 },
65 },
66 {
67 name: "SQLInstance treats disk size as GCP-managed when disk autoresize set",
68 kind: "SQLInstance",
69 lastAppliedConfig: map[string]interface{}{
70 "spec": map[string]interface{}{
71 "otherField": "otherValue",
72 "settings": map[string]interface{}{
73 "diskAutoresize": true,
74 },
75 },
76 },
77 resourceExists: true,
78 inputConfig: map[string]interface{}{
79 "otherField": "otherValue",
80 "settings": map[string]interface{}{
81 "diskAutoresize": true,
82 "diskSize": 50,
83 },
84 },
85 expectedConfig: map[string]interface{}{
86 "otherField": "otherValue",
87 "settings": map[string]interface{}{
88 "diskAutoresize": true,
89 },
90 },
91 },
92 {
93 name: "ComputeBackendService treats backends as unmanaged when backends not set",
94 kind: "ComputeBackendService",
95 lastAppliedConfig: map[string]interface{}{
96 "spec": map[string]interface{}{
97 "otherField": "otherValue",
98 },
99 },
100 resourceExists: true,
101 inputConfig: map[string]interface{}{
102 "otherField": "otherValue",
103 "backend": []interface{}{},
104 },
105 expectedConfig: map[string]interface{}{
106 "otherField": "otherValue",
107 },
108 },
109 {
110 name: "ComputeBackendService manages user-supplied backends",
111 kind: "ComputeBackendService",
112 lastAppliedConfig: map[string]interface{}{
113 "spec": map[string]interface{}{
114 "otherField": "otherValue",
115 "backend": []interface{}{
116 "backend1",
117 },
118 },
119 },
120 resourceExists: true,
121 inputConfig: map[string]interface{}{
122 "otherField": "otherValue",
123 "backend": []interface{}{
124 "backend1",
125 },
126 },
127 expectedConfig: map[string]interface{}{
128 "otherField": "otherValue",
129 "backend": []interface{}{
130 "backend1",
131 },
132 },
133 },
134 {
135 name: "user-applied value is explicit override of GCP",
136 kind: "ContainerCluster",
137 lastAppliedConfig: map[string]interface{}{
138 "spec": map[string]interface{}{
139 "otherField": "otherValue",
140 "releaseChannel": map[string]interface{}{
141 "channel": "REGULAR",
142 },
143 "nodeVersion": "1.14",
144 },
145 },
146 resourceExists: true,
147 inputConfig: map[string]interface{}{
148 "otherField": "otherValue",
149 "releaseChannel": map[string]interface{}{
150 "channel": "REGULAR",
151 },
152 "nodeVersion": "1.14",
153 },
154 expectedConfig: map[string]interface{}{
155 "otherField": "otherValue",
156 "releaseChannel": map[string]interface{}{
157 "channel": "REGULAR",
158 },
159 "nodeVersion": "1.14",
160 },
161 },
162 {
163 name: "use explicit config when creating resource",
164 kind: "ContainerCluster",
165 resourceExists: false,
166 inputConfig: map[string]interface{}{
167 "otherField": "otherValue",
168 "releaseChannel": map[string]interface{}{
169 "channel": "REGULAR",
170 },
171 "nodeVersion": "1.14",
172 },
173 expectedConfig: map[string]interface{}{
174 "otherField": "otherValue",
175 "releaseChannel": map[string]interface{}{
176 "channel": "REGULAR",
177 },
178 "nodeVersion": "1.14",
179 },
180 },
181 {
182 name: "ContainerNodePool treats version field as GCP-managed when autoUpgrade set",
183 kind: "ContainerNodePool",
184 lastAppliedConfig: map[string]interface{}{
185 "spec": map[string]interface{}{
186 "otherField": "otherValue",
187 "management": map[string]interface{}{
188 "autoUpgrade": true,
189 },
190 },
191 },
192 resourceExists: true,
193 inputConfig: map[string]interface{}{
194 "otherField": "otherValue",
195 "management": map[string]interface{}{
196 "autoUpgrade": true,
197 },
198 "version": "1.18.0-gke.0",
199 },
200 expectedConfig: map[string]interface{}{
201 "otherField": "otherValue",
202 "management": map[string]interface{}{
203 "autoUpgrade": true,
204 },
205 },
206 },
207 {
208 name: "ContainerNodePool uses spec value when autoUpgrade turned off",
209 kind: "ContainerNodePool",
210 lastAppliedConfig: map[string]interface{}{
211 "spec": map[string]interface{}{
212 "otherField": "otherValue",
213 "management": map[string]interface{}{
214 "autoUpgrade": false,
215 },
216 },
217 },
218 resourceExists: true,
219 inputConfig: map[string]interface{}{
220 "otherField": "otherValue",
221 "management": map[string]interface{}{
222 "autoUpgrade": false,
223 },
224 "version": "1.18.0-gke.0",
225 },
226 expectedConfig: map[string]interface{}{
227 "otherField": "otherValue",
228 "management": map[string]interface{}{
229 "autoUpgrade": false,
230 },
231 "version": "1.18.0-gke.0",
232 },
233 },
234
235 {
236 name: "ContainerNodePool uses user-supplied version when explicitly applied",
237 kind: "ContainerNodePool",
238 lastAppliedConfig: map[string]interface{}{
239 "spec": map[string]interface{}{
240 "otherField": "otherValue",
241 "management": map[string]interface{}{
242 "autoUpgrade": true,
243 },
244 "version": "1.18.0-gke.0",
245 },
246 },
247 resourceExists: true,
248 inputConfig: map[string]interface{}{
249 "otherField": "otherValue",
250 "management": map[string]interface{}{
251 "autoUpgrade": true,
252 },
253 "version": "1.18.0-gke.0",
254 },
255 expectedConfig: map[string]interface{}{
256 "otherField": "otherValue",
257 "management": map[string]interface{}{
258 "autoUpgrade": true,
259 },
260 "version": "1.18.0-gke.0",
261 },
262 },
263 {
264 name: "ContainerNodePool treats initialNodeCount as GCP-managed",
265 kind: "ContainerNodePool",
266 lastAppliedConfig: map[string]interface{}{
267 "spec": map[string]interface{}{
268 "otherField": "otherValue",
269 },
270 },
271 resourceExists: true,
272 inputConfig: map[string]interface{}{
273 "otherField": "otherValue",
274 "initialNodeCount": 1,
275 },
276 expectedConfig: map[string]interface{}{
277 "otherField": "otherValue",
278 },
279 },
280 {
281 name: "ContainerNodePool treats nodeCount as GCP-managed if autoscaling set",
282 kind: "ContainerNodePool",
283 lastAppliedConfig: map[string]interface{}{
284 "spec": map[string]interface{}{
285 "otherField": "otherValue",
286 "autoscaling": map[string]interface{}{
287 "minNodeCount": 1,
288 "maxNodeCount": 3,
289 },
290 },
291 },
292 resourceExists: true,
293 inputConfig: map[string]interface{}{
294 "otherField": "otherValue",
295 "autoscaling": map[string]interface{}{
296 "minNodeCount": 1,
297 "maxNodeCount": 3,
298 },
299 "nodeCount": 1,
300 },
301 expectedConfig: map[string]interface{}{
302 "otherField": "otherValue",
303 "autoscaling": map[string]interface{}{
304 "minNodeCount": 1,
305 "maxNodeCount": 3,
306 },
307 },
308 },
309 {
310 name: "ContainerNodePool uses explicit nodeCount when autoscaling disabled",
311 kind: "ContainerNodePool",
312 lastAppliedConfig: map[string]interface{}{
313 "spec": map[string]interface{}{
314 "otherField": "otherValue",
315 },
316 },
317 resourceExists: true,
318 inputConfig: map[string]interface{}{
319 "otherField": "otherValue",
320 "nodeCount": 1,
321 },
322 expectedConfig: map[string]interface{}{
323 "otherField": "otherValue",
324 "nodeCount": 1,
325 },
326 },
327 {
328 name: "ContainerNodePool uses explicit nodeCount when autoscaling set explicitly to nil",
329 kind: "ContainerNodePool",
330 lastAppliedConfig: map[string]interface{}{
331 "spec": map[string]interface{}{
332 "otherField": "otherValue",
333 "autoscaling": nil,
334 },
335 },
336 resourceExists: true,
337 inputConfig: map[string]interface{}{
338 "otherField": "otherValue",
339 "autoscaling": nil,
340 "nodeCount": 1,
341 },
342 expectedConfig: map[string]interface{}{
343 "otherField": "otherValue",
344 "autoscaling": nil,
345 "nodeCount": 1,
346 },
347 },
348 {
349 name: "BigtableInstance removes numNodes when not set",
350 kind: "BigtableInstance",
351 lastAppliedConfig: map[string]interface{}{
352 "spec": map[string]interface{}{
353 "otherField": "otherValue",
354 "cluster": []interface{}{
355 map[string]interface{}{
356 "clusterId": "test1",
357 "zone": "us-central1-a",
358 },
359 map[string]interface{}{
360 "clusterId": "test2",
361 "zone": "us-west1-a",
362 },
363 },
364 },
365 },
366 resourceExists: true,
367 inputConfig: map[string]interface{}{
368 "otherField": "otherValue",
369 "cluster": []interface{}{
370 map[string]interface{}{
371 "clusterId": "test1",
372 "zone": "us-central1-a",
373 "numNodes": float64(1),
374 },
375 map[string]interface{}{
376 "clusterId": "test2",
377 "zone": "us-west1-a",
378 "numNodes": float64(1),
379 },
380 },
381 },
382 expectedConfig: map[string]interface{}{
383 "otherField": "otherValue",
384 "cluster": []interface{}{
385 map[string]interface{}{
386 "clusterId": "test1",
387 "zone": "us-central1-a",
388 },
389 map[string]interface{}{
390 "clusterId": "test2",
391 "zone": "us-west1-a",
392 },
393 },
394 },
395 },
396 {
397 name: "BigtableInstance respects numNodes when specified",
398 kind: "BigtableInstance",
399 lastAppliedConfig: map[string]interface{}{
400 "spec": map[string]interface{}{
401 "otherField": "otherValue",
402 "cluster": []interface{}{
403 map[string]interface{}{
404 "clusterId": "test1",
405 "zone": "us-central1-a",
406 "numNodes": float64(2),
407 },
408 map[string]interface{}{
409 "clusterId": "test2",
410 "zone": "us-west1-a",
411 "numNodes": float64(3),
412 },
413 },
414 },
415 },
416 resourceExists: true,
417 inputConfig: map[string]interface{}{
418 "otherField": "otherValue",
419 "cluster": []interface{}{
420 map[string]interface{}{
421 "clusterId": "test1",
422 "zone": "us-central1-a",
423 "numNodes": float64(2),
424 },
425 map[string]interface{}{
426 "clusterId": "test2",
427 "zone": "us-west1-a",
428 "numNodes": float64(3),
429 },
430 },
431 },
432 expectedConfig: map[string]interface{}{
433 "otherField": "otherValue",
434 "cluster": []interface{}{
435 map[string]interface{}{
436 "clusterId": "test1",
437 "zone": "us-central1-a",
438 "numNodes": float64(2),
439 },
440 map[string]interface{}{
441 "clusterId": "test2",
442 "zone": "us-west1-a",
443 "numNodes": float64(3),
444 },
445 },
446 },
447 },
448 {
449 name: "BigtableInstance handles mixed set and unset numNodes",
450 kind: "BigtableInstance",
451 lastAppliedConfig: map[string]interface{}{
452 "spec": map[string]interface{}{
453 "otherField": "otherValue",
454 "cluster": []interface{}{
455 map[string]interface{}{
456 "clusterId": "test1",
457 "zone": "us-central1-a",
458 "numNodes": float64(2),
459 },
460 map[string]interface{}{
461 "clusterId": "test2",
462 "zone": "us-west1-a",
463 },
464 },
465 },
466 },
467 resourceExists: true,
468 inputConfig: map[string]interface{}{
469 "otherField": "otherValue",
470 "cluster": []interface{}{
471 map[string]interface{}{
472 "clusterId": "test1",
473 "zone": "us-central1-a",
474 "numNodes": float64(2),
475 },
476 map[string]interface{}{
477 "clusterId": "test2",
478 "zone": "us-west1-a",
479 "numNodes": float64(3),
480 },
481 },
482 },
483 expectedConfig: map[string]interface{}{
484 "otherField": "otherValue",
485 "cluster": []interface{}{
486 map[string]interface{}{
487 "clusterId": "test1",
488 "zone": "us-central1-a",
489 "numNodes": float64(2),
490 },
491 map[string]interface{}{
492 "clusterId": "test2",
493 "zone": "us-west1-a",
494 },
495 },
496 },
497 },
498 {
499 name: "BigtableInstance removes an existing numNodes, if the value is removed",
500 kind: "BigtableInstance",
501 lastAppliedConfig: map[string]interface{}{
502 "spec": map[string]interface{}{
503 "otherField": "otherValue",
504 "cluster": []interface{}{
505 map[string]interface{}{
506 "clusterId": "test1",
507 "zone": "us-central1-a",
508 "numNodes": float64(2),
509 },
510 },
511 },
512 },
513 resourceExists: true,
514 inputConfig: map[string]interface{}{
515 "otherField": "otherValue",
516 "cluster": []interface{}{
517 map[string]interface{}{
518 "clusterId": "test1",
519 "zone": "us-central1-a",
520 },
521 },
522 },
523 expectedConfig: map[string]interface{}{
524 "otherField": "otherValue",
525 "cluster": []interface{}{
526 map[string]interface{}{
527 "clusterId": "test1",
528 "zone": "us-central1-a",
529 },
530 },
531 },
532 },
533 }
534 for _, tc := range tests {
535 t.Run(tc.name, func(t *testing.T) {
536 r := resourceSkeleton()
537 r.SetGroupVersionKind(schema.GroupVersionKind{Kind: tc.kind})
538 lastAppliedConfigJSON, err := json.Marshal(tc.lastAppliedConfig)
539 if err != nil {
540 t.Fatalf("error marshaling last applied config: %v", err)
541 }
542 r.SetAnnotations(map[string]string{
543 k8s.LastAppliedConfigurationAnnotation: string(lastAppliedConfigJSON),
544 })
545 var liveState *terraform.InstanceState
546 if tc.resourceExists {
547
548
549
550
551 liveState = &terraform.InstanceState{
552 ID: "foo",
553 }
554 }
555 config := tc.inputConfig
556 if err := ResolveLegacyGCPManagedFields(r, liveState, config); err != nil {
557 t.Fatalf("error resolving GCP-managed fields: %v", err)
558 }
559 if !test.Equals(t, tc.expectedConfig, config) {
560 t.Fatalf("expected config: %+v, actual: %+v", tc.expectedConfig, config)
561 }
562 })
563 }
564 }
565
View as plain text