1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package crdgeneration
16
17 import (
18 "testing"
19
20 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
21 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/crd/crdgeneration/crdboilerplate"
22 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
23
24 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
25 apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
26 k8sschema "k8s.io/apimachinery/pkg/runtime/schema"
27 )
28
29 var sensitiveSchemaBoilerplate = crdboilerplate.GetSensitiveFieldSchemaBoilerplate()
30
31 func TestTFSchemaToJSONSchema(t *testing.T) {
32 tests := []struct {
33 name string
34 tf schema.Schema
35 json apiextensions.JSONSchemaProps
36 }{
37 {
38 name: "primitive field",
39 tf: schema.Schema{
40 Type: schema.TypeString,
41 },
42 json: apiextensions.JSONSchemaProps{
43 Type: "string",
44 },
45 },
46 {
47 name: "map with no value schema",
48 tf: schema.Schema{
49 Type: schema.TypeMap,
50 },
51 json: apiextensions.JSONSchemaProps{
52 Type: "object",
53 },
54 },
55 {
56 name: "sensitive, configurable (required) string field",
57 tf: schema.Schema{
58 Type: schema.TypeString,
59 Sensitive: true,
60 Required: true,
61 },
62 json: sensitiveSchemaBoilerplate,
63 },
64 {
65 name: "sensitive, configurable (optional) string field",
66 tf: schema.Schema{
67 Type: schema.TypeString,
68 Sensitive: true,
69 Optional: true,
70 },
71 json: sensitiveSchemaBoilerplate,
72 },
73 {
74 name: "sensitive, non-configurable string field",
75 tf: schema.Schema{
76 Type: schema.TypeString,
77 Sensitive: true,
78 },
79 json: apiextensions.JSONSchemaProps{
80 Type: "string",
81 },
82 },
83 {
84 name: "nested sensitive, configurable (required) string field",
85 tf: schema.Schema{
86 Type: schema.TypeList,
87 MaxItems: 1,
88 Elem: &schema.Resource{
89 Schema: map[string]*schema.Schema{
90 "sensitive_field": {
91 Type: schema.TypeString,
92 Sensitive: true,
93 Required: true,
94 },
95 },
96 },
97 },
98 json: apiextensions.JSONSchemaProps{
99 Type: "object",
100 Properties: map[string]apiextensions.JSONSchemaProps{
101 "sensitiveField": sensitiveSchemaBoilerplate,
102 },
103 Required: []string{"sensitiveField"},
104 },
105 },
106 {
107 name: "nested sensitive, configurable (optional) string field",
108 tf: schema.Schema{
109 Type: schema.TypeList,
110 MaxItems: 1,
111 Elem: &schema.Resource{
112 Schema: map[string]*schema.Schema{
113 "sensitive_field": {
114 Type: schema.TypeString,
115 Sensitive: true,
116 Optional: true,
117 },
118 },
119 },
120 },
121 json: apiextensions.JSONSchemaProps{
122 Type: "object",
123 Properties: map[string]apiextensions.JSONSchemaProps{
124 "sensitiveField": sensitiveSchemaBoilerplate,
125 },
126 },
127 },
128 {
129 name: "nested sensitive, non-configurable string field",
130 tf: schema.Schema{
131 Type: schema.TypeList,
132 MaxItems: 1,
133 Elem: &schema.Resource{
134 Schema: map[string]*schema.Schema{
135 "sensitive_field": {
136 Type: schema.TypeString,
137 Sensitive: true,
138 },
139 },
140 },
141 },
142 json: apiextensions.JSONSchemaProps{
143 Type: "object",
144 Properties: map[string]apiextensions.JSONSchemaProps{
145 "sensitiveField": apiextensions.JSONSchemaProps{
146 Type: "string",
147 },
148 },
149 },
150 },
151 }
152 for _, tc := range tests {
153 t.Run(tc.name, func(t *testing.T) {
154 out := tfSchemaToJSONSchema(&tc.tf)
155 if !test.Equals(t, tc.json, out) {
156 t.Fatalf("\n\nexpected: %v,\n\nactual: %v", tc.json, *out)
157 }
158 })
159 }
160 }
161
162 func TestHandleResourceReference(t *testing.T) {
163 tests := []struct {
164 name string
165 refConfig v1alpha1.ReferenceConfig
166 in apiextensions.JSONSchemaProps
167 out apiextensions.JSONSchemaProps
168 }{
169 {
170 name: "embedded reference",
171 refConfig: v1alpha1.ReferenceConfig{
172 TFField: "bar",
173 TypeConfig: v1alpha1.TypeConfig{
174 Key: "barRef",
175 GVK: k8sschema.GroupVersionKind{
176 Kind: "Bar",
177 },
178 },
179 },
180 in: apiextensions.JSONSchemaProps{
181 Properties: map[string]apiextensions.JSONSchemaProps{
182 "bar": {Type: "string"},
183 },
184 },
185 out: apiextensions.JSONSchemaProps{
186 Properties: map[string]apiextensions.JSONSchemaProps{
187 "barRef": *GetResourceReferenceSchemaFromTypeConfig(v1alpha1.TypeConfig{
188 Key: "barRef",
189 GVK: k8sschema.GroupVersionKind{
190 Kind: "Bar",
191 },
192 }),
193 },
194 },
195 },
196 {
197 name: "embedded reference, required",
198 refConfig: v1alpha1.ReferenceConfig{
199 TFField: "bar",
200 TypeConfig: v1alpha1.TypeConfig{
201 Key: "barRef",
202 GVK: k8sschema.GroupVersionKind{
203 Kind: "Bar",
204 },
205 },
206 },
207 in: apiextensions.JSONSchemaProps{
208 Properties: map[string]apiextensions.JSONSchemaProps{
209 "bar": {Type: "string"},
210 },
211 Required: []string{"bar"},
212 },
213 out: apiextensions.JSONSchemaProps{
214 Properties: map[string]apiextensions.JSONSchemaProps{
215 "barRef": *GetResourceReferenceSchemaFromTypeConfig(v1alpha1.TypeConfig{
216 Key: "barRef",
217 GVK: k8sschema.GroupVersionKind{
218 Kind: "Bar",
219 },
220 }),
221 },
222 Required: []string{"barRef"},
223 },
224 },
225 {
226 name: "complex reference",
227 refConfig: v1alpha1.ReferenceConfig{
228 TFField: "bar",
229 Types: []v1alpha1.TypeConfig{
230 {
231 Key: "barRef",
232 GVK: k8sschema.GroupVersionKind{
233 Kind: "Bar",
234 },
235 },
236 {
237 Key: "value",
238 JSONSchemaType: "string",
239 },
240 },
241 },
242 in: apiextensions.JSONSchemaProps{
243 Properties: map[string]apiextensions.JSONSchemaProps{
244 "bar": {Type: "string"},
245 },
246 },
247 out: apiextensions.JSONSchemaProps{
248 Properties: map[string]apiextensions.JSONSchemaProps{
249 "bar": {
250 Type: "object",
251 Properties: map[string]apiextensions.JSONSchemaProps{
252 "barRef": *GetResourceReferenceSchemaFromTypeConfig(v1alpha1.TypeConfig{
253 Key: "barRef",
254 GVK: k8sschema.GroupVersionKind{
255 Kind: "Bar",
256 },
257 }),
258 "value": {Type: "string"},
259 },
260 },
261 },
262 },
263 },
264 {
265 name: "reference nested in object",
266 refConfig: v1alpha1.ReferenceConfig{
267 TFField: "bar.name",
268 TypeConfig: v1alpha1.TypeConfig{
269 Key: "barRef",
270 GVK: k8sschema.GroupVersionKind{
271 Kind: "Bar",
272 },
273 },
274 },
275 in: apiextensions.JSONSchemaProps{
276 Properties: map[string]apiextensions.JSONSchemaProps{
277 "bar": {
278 Type: "object",
279 Properties: map[string]apiextensions.JSONSchemaProps{
280 "name": {Type: "string"},
281 },
282 },
283 },
284 },
285 out: apiextensions.JSONSchemaProps{
286 Properties: map[string]apiextensions.JSONSchemaProps{
287 "bar": {
288 Type: "object",
289 Properties: map[string]apiextensions.JSONSchemaProps{
290 "barRef": *GetResourceReferenceSchemaFromTypeConfig(v1alpha1.TypeConfig{
291 Key: "barRef",
292 GVK: k8sschema.GroupVersionKind{
293 Kind: "Bar",
294 },
295 }),
296 },
297 },
298 },
299 },
300 },
301 {
302 name: "reference nested in list of objects",
303 refConfig: v1alpha1.ReferenceConfig{
304 TFField: "bar.name",
305 TypeConfig: v1alpha1.TypeConfig{
306 Key: "barRef",
307 GVK: k8sschema.GroupVersionKind{
308 Kind: "Bar",
309 },
310 },
311 },
312 in: apiextensions.JSONSchemaProps{
313 Properties: map[string]apiextensions.JSONSchemaProps{
314 "bar": {
315 Type: "array",
316 Items: &apiextensions.JSONSchemaPropsOrArray{
317 Schema: &apiextensions.JSONSchemaProps{
318 Properties: map[string]apiextensions.JSONSchemaProps{
319 "name": {Type: "string"},
320 },
321 },
322 },
323 },
324 },
325 },
326 out: apiextensions.JSONSchemaProps{
327 Properties: map[string]apiextensions.JSONSchemaProps{
328 "bar": {
329 Type: "array",
330 Items: &apiextensions.JSONSchemaPropsOrArray{
331 Schema: &apiextensions.JSONSchemaProps{
332 Properties: map[string]apiextensions.JSONSchemaProps{
333 "barRef": *GetResourceReferenceSchemaFromTypeConfig(v1alpha1.TypeConfig{
334 Key: "barRef",
335 GVK: k8sschema.GroupVersionKind{
336 Kind: "Bar",
337 },
338 }),
339 },
340 },
341 },
342 },
343 },
344 },
345 },
346 {
347 name: "list of references",
348 refConfig: v1alpha1.ReferenceConfig{
349 TFField: "bars",
350 TypeConfig: v1alpha1.TypeConfig{
351 GVK: k8sschema.GroupVersionKind{
352 Kind: "Bar",
353 },
354 },
355 },
356 in: apiextensions.JSONSchemaProps{
357 Properties: map[string]apiextensions.JSONSchemaProps{
358 "bars": {
359 Type: "array",
360 Items: &apiextensions.JSONSchemaPropsOrArray{
361 Schema: &apiextensions.JSONSchemaProps{
362 Type: "string",
363 },
364 },
365 },
366 },
367 },
368 out: apiextensions.JSONSchemaProps{
369 Properties: map[string]apiextensions.JSONSchemaProps{
370 "bars": {
371 Type: "array",
372 Items: &apiextensions.JSONSchemaPropsOrArray{
373 Schema: GetResourceReferenceSchemaFromTypeConfig(v1alpha1.TypeConfig{
374 Key: "barRef",
375 GVK: k8sschema.GroupVersionKind{
376 Kind: "Bar",
377 },
378 }),
379 },
380 },
381 },
382 },
383 },
384 }
385
386 for _, tc := range tests {
387 t.Run(tc.name, func(t *testing.T) {
388 handleResourceReference(tc.refConfig, &tc.in)
389 if !test.Equals(t, tc.in, tc.out) {
390 t.Fatalf("expected: %v, actual: %v", tc.out, tc.in)
391 }
392 })
393 }
394 }
395
396 func TestGetDescriptionForExternalRef(t *testing.T) {
397 tests := []struct {
398 name string
399 typeConfig v1alpha1.TypeConfig
400 want string
401 }{
402 {
403 name: "refWithValueTemplate",
404 typeConfig: v1alpha1.TypeConfig{
405 TargetField: "email",
406 ValueTemplate: "projects/{{project}}/serviceAccounts/{{value}}",
407 GVK: k8sschema.GroupVersionKind{
408 Kind: "IAMServiceAccount",
409 },
410 },
411 want: "Allowed value: string of the format `projects/{{project}}/serviceAccounts/{{value}}`, where {{value}} is the `email` field of an `IAMServiceAccount` resource.",
412 },
413 {
414 name: "refWithoutValueTemplate",
415 typeConfig: v1alpha1.TypeConfig{
416 TargetField: "email",
417 GVK: k8sschema.GroupVersionKind{
418 Kind: "IAMServiceAccount",
419 },
420 },
421 want: "Allowed value: The `email` field of an `IAMServiceAccount` resource.",
422 },
423 }
424
425 for _, tc := range tests {
426 t.Run(tc.name, func(t *testing.T) {
427 got := getDescriptionForExternalRef(tc.typeConfig)
428 if got != tc.want {
429 t.Errorf("getDescriptionForExternalRef(%v) = '%v', want: '%v'", tc.typeConfig, got, tc.want)
430 }
431 })
432 }
433 }
434
View as plain text