1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package test
16
17 import (
18 "context"
19 "fmt"
20 "strings"
21 "testing"
22
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
24 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
25 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/text"
26
27 corev1 "k8s.io/api/core/v1"
28 apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
29 "k8s.io/apimachinery/pkg/api/errors"
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32 "sigs.k8s.io/controller-runtime/pkg/client"
33 )
34
35 const Namespace = "namespace-1"
36
37 func FakeCRDs() []*apiextensions.CustomResourceDefinition {
38 return []*apiextensions.CustomResourceDefinition{
39 CRDForGVK(metav1.GroupVersionKind{
40 Group: "test1.cnrm.cloud.google.com",
41 Version: "v1alpha1",
42 Kind: "Test1Foo",
43 }),
44 CRDForGVK(metav1.GroupVersionKind{
45 Group: "test1.cnrm.cloud.google.com",
46 Version: "v1alpha1",
47 Kind: "Test1Bar",
48 }),
49 CRDForGVK(metav1.GroupVersionKind{
50
51 Group: "test2.cnrm.cloud.google.com",
52 Version: "v1alpha1",
53 Kind: "Test2Baz",
54 }),
55 CRDForGVK(metav1.GroupVersionKind{
56 Group: "test3.cnrm.cloud.google.com",
57 Version: "v1alpha1",
58 Kind: "Test3UserSpecifiedResourceIDKind",
59 }),
60 CRDForGVK(metav1.GroupVersionKind{
61 Group: "test3.cnrm.cloud.google.com",
62 Version: "v1alpha1",
63 Kind: "Test3ServerGeneratedResourceIDKind",
64 }),
65 CRDForGVK(metav1.GroupVersionKind{
66 Group: "test4.cnrm.cloud.google.com",
67 Version: "v1alpha1",
68 Kind: "Test4DCLResourceServerGeneratedResourceIDKind",
69 }),
70 CRDForGVK(metav1.GroupVersionKind{
71 Group: "test4.cnrm.cloud.google.com",
72 Version: "v1alpha1",
73 Kind: "Test4DCLResourceUserSpecifiedResourceIDKind",
74 }),
75 }
76 }
77
78
79
80
81 func FakeCRDsWithHierarchicalResources() []*apiextensions.CustomResourceDefinition {
82 return append(FakeCRDs(),
83 CRDForGVK(metav1.GroupVersionKind{
84 Group: "resourcemanager.cnrm.cloud.google.com",
85 Version: "v1beta1",
86 Kind: "Project",
87 }),
88 CRDForGVK(metav1.GroupVersionKind{
89 Group: "resourcemanager.cnrm.cloud.google.com",
90 Version: "v1beta1",
91 Kind: "Folder",
92 }),
93 )
94 }
95
96 func FakeServiceMappings() []v1alpha1.ServiceMapping {
97 var test1FooReconciliationIntervalInSeconds uint32 = 100
98 return []v1alpha1.ServiceMapping{
99 {
100 ObjectMeta: metav1.ObjectMeta{
101 Namespace: "cnrm-system",
102 Name: "test1.cnrm.cloud.google.com",
103 },
104 Spec: v1alpha1.ServiceMappingSpec{
105 Name: "test1",
106 ServiceHostName: "test1",
107 Version: "v1alpha1",
108 Resources: []v1alpha1.ResourceConfig{
109 {
110 Name: "foo",
111 Kind: "Test1Foo",
112 ReconciliationIntervalInSeconds: &test1FooReconciliationIntervalInSeconds,
113 },
114 {
115 Name: "bar",
116 Kind: "Test1Bar",
117 },
118 {
119 Name: "fake_tf_based_resource",
120 Kind: "Test1FakeTFBasedResource",
121 },
122 },
123 },
124 },
125 {
126 ObjectMeta: metav1.ObjectMeta{
127 Namespace: "cnrm-system",
128 Name: "test2.cnrm.cloud.google.com",
129 },
130 Spec: v1alpha1.ServiceMappingSpec{
131 Name: "test2",
132 ServiceHostName: "test2",
133 Version: "v1alpha1",
134 Resources: []v1alpha1.ResourceConfig{
135 {
136 Name: "baz",
137 Kind: "Test2Baz",
138 },
139 },
140 },
141 },
142 {
143 ObjectMeta: metav1.ObjectMeta{
144 Namespace: "cnrm-system",
145 Name: "test3.cnrm.cloud.google.com",
146 },
147 Spec: v1alpha1.ServiceMappingSpec{
148 Name: "test3",
149 ServiceHostName: "test3",
150 Version: "v1alpha1",
151 Resources: []v1alpha1.ResourceConfig{
152 {
153 Name: "user_specified_resource_id_kind",
154 Kind: "Test3UserSpecifiedResourceIDKind",
155 ResourceID: v1alpha1.ResourceID{
156 TargetField: "resource_id_field",
157 },
158 MetadataMapping: v1alpha1.MetadataMapping{
159 Name: "resource_id_field",
160 },
161 IDTemplate: "{{resource_id_field}}",
162 },
163 {
164 Name: "server_generated_resource_id_kind_with_value_template",
165 Kind: "Test3ServerGeneratedResourceIDKind",
166 ResourceID: v1alpha1.ResourceID{
167 TargetField: "resource_id_field",
168 ValueTemplate: "values/{{value}}",
169 },
170 ServerGeneratedIDField: "resource_id_field",
171 IDTemplate: "{{resource_id_field}}",
172 },
173 },
174 },
175 },
176 {
177 ObjectMeta: metav1.ObjectMeta{
178 Namespace: "cnrm-system",
179 Name: "test4.cnrm.cloud.google.com",
180 },
181 Spec: v1alpha1.ServiceMappingSpec{
182 Name: "test4",
183 ServiceHostName: "test4",
184 Version: "v1alpha1",
185 Resources: []v1alpha1.ResourceConfig{
186 {
187 Kind: "Test4ProjectScopedResource",
188 Containers: []v1alpha1.Container{
189 {Type: v1alpha1.ContainerTypeProject},
190 },
191 HierarchicalReferences: []v1alpha1.HierarchicalReference{
192 {
193 Type: v1alpha1.HierarchicalReferenceTypeProject,
194 Key: "projectRef",
195 },
196 },
197 },
198 {
199 Kind: "Test4ProjectScopedResourceWithOnlyContainerSupport",
200 Containers: []v1alpha1.Container{
201 {Type: v1alpha1.ContainerTypeProject},
202 },
203 },
204 {
205 Kind: "Test4ProjectScopedResourceWithOnlyHierarchicalReferenceSupport",
206 Containers: []v1alpha1.Container{
207 {Type: v1alpha1.ContainerTypeProject},
208 },
209 HierarchicalReferences: []v1alpha1.HierarchicalReference{
210 {
211 Type: v1alpha1.HierarchicalReferenceTypeProject,
212 Key: "projectRef",
213 },
214 },
215 },
216 {
217 Kind: "Test4MultiParentResource",
218 Containers: []v1alpha1.Container{
219 {Type: v1alpha1.ContainerTypeFolder},
220 {Type: v1alpha1.ContainerTypeOrganization},
221 },
222 HierarchicalReferences: []v1alpha1.HierarchicalReference{
223 {
224 Type: v1alpha1.HierarchicalReferenceTypeFolder,
225 Key: "folderRef",
226 },
227 {
228 Type: v1alpha1.HierarchicalReferenceTypeOrganization,
229 Key: "organizationRef",
230 },
231 },
232 },
233 {
234 Kind: "Test4MultiParentResourceWithOnlyContainerSupport",
235 Containers: []v1alpha1.Container{
236 {Type: v1alpha1.ContainerTypeFolder},
237 {Type: v1alpha1.ContainerTypeOrganization},
238 },
239 },
240 {
241 Kind: "Test4MultiParentResourceWithOnlyHierarchicalReferenceSupport",
242 HierarchicalReferences: []v1alpha1.HierarchicalReference{
243 {
244 Type: v1alpha1.HierarchicalReferenceTypeFolder,
245 Key: "folderRef",
246 },
247 {
248 Type: v1alpha1.HierarchicalReferenceTypeOrganization,
249 Key: "organizationRef",
250 },
251 },
252 },
253 {
254 Kind: "Test4NoParentResource",
255 },
256 },
257 },
258 },
259 }
260 }
261
262
263
264
265 func FakeServiceMappingsWithHierarchicalResources() []v1alpha1.ServiceMapping {
266 return append(FakeServiceMappings(),
267 v1alpha1.ServiceMapping{
268 ObjectMeta: metav1.ObjectMeta{
269 Namespace: "cnrm-system",
270 Name: "resourcemanager.cnrm.cloud.google.com",
271 },
272 Spec: v1alpha1.ServiceMappingSpec{
273 Name: "ResourceManager",
274 Version: "v1beta1",
275 ServiceHostName: "cloudresourcemanager.googleapis.com",
276 Resources: []v1alpha1.ResourceConfig{
277 {
278 Kind: "Project",
279 MetadataMapping: v1alpha1.MetadataMapping{
280 Name: "project_id",
281 },
282 ResourceID: v1alpha1.ResourceID{
283 TargetField: "project_id",
284 },
285 },
286 {
287 Kind: "Folder",
288 },
289 },
290 },
291 })
292 }
293
294 func NewBarUnstructured(name, ns string, readyStatus corev1.ConditionStatus) *unstructured.Unstructured {
295 return &unstructured.Unstructured{
296 Object: map[string]interface{}{
297 "apiVersion": "test1.cnrm.cloud.google.com/v1alpha1",
298 "kind": "Test1Bar",
299 "metadata": map[string]interface{}{
300 "annotations": map[string]interface{}{
301 k8s.ProjectIDAnnotation: "my-project-1",
302 },
303 "name": name,
304 "namespace": ns,
305 },
306 "spec": map[string]interface{}{
307 "location": "test-location",
308 "specField": "abc123",
309 },
310 "status": map[string]interface{}{
311 "conditions": []interface{}{
312 map[string]interface{}{
313 "type": "Ready",
314 "status": readyStatus,
315 },
316 },
317 "statusField": "foobar",
318 },
319 },
320 }
321 }
322
323 func NewIAMServiceAccountUnstructured(name, namespace string) *unstructured.Unstructured {
324 return &unstructured.Unstructured{
325 Object: map[string]interface{}{
326 "apiVersion": "iam.cnrm.cloud.google.com/v1beta1",
327 "kind": "IAMServiceAccount",
328 "metadata": map[string]interface{}{
329 "name": name,
330 "namespace": namespace,
331 },
332 },
333 }
334
335 }
336
337 func NewProjectUnstructured(name, projectID string, readyStatus corev1.ConditionStatus) *unstructured.Unstructured {
338 return &unstructured.Unstructured{
339 Object: map[string]interface{}{
340 "apiVersion": "resourcemanager.cnrm.cloud.google.com/v1beta1",
341 "kind": "Project",
342 "metadata": map[string]interface{}{
343 "name": name,
344 },
345 "spec": map[string]interface{}{
346 "resourceID": projectID,
347 },
348 "status": map[string]interface{}{
349 "conditions": []interface{}{
350 map[string]interface{}{
351 "type": "Ready",
352 "status": readyStatus,
353 },
354 },
355 },
356 },
357 }
358 }
359
360 func NewFolderUnstructured(name, folderID string, readyStatus corev1.ConditionStatus) *unstructured.Unstructured {
361 return &unstructured.Unstructured{
362 Object: map[string]interface{}{
363 "apiVersion": "resourcemanager.cnrm.cloud.google.com/v1beta1",
364 "kind": "Folder",
365 "metadata": map[string]interface{}{
366 "name": name,
367 },
368 "spec": map[string]interface{}{
369 "resourceID": folderID,
370 },
371 "status": map[string]interface{}{
372 "conditions": []interface{}{
373 map[string]interface{}{
374 "type": "Ready",
375 "status": readyStatus,
376 },
377 },
378 "folderId": folderID,
379 },
380 },
381 }
382 }
383
384 func NewSecretUnstructured(name, ns string, stringData map[string]interface{}) *unstructured.Unstructured {
385 return &unstructured.Unstructured{
386 Object: map[string]interface{}{
387 "apiVersion": "v1",
388 "kind": "Secret",
389 "metadata": map[string]interface{}{
390 "name": name,
391 "namespace": ns,
392 },
393 "stringData": stringData,
394 },
395 }
396 }
397
398 func EnsureObjectsExist(t *testing.T, objs []*unstructured.Unstructured, c client.Client) {
399 t.Helper()
400 for _, obj := range objs {
401 EnsureObjectExists(t, obj, c)
402 }
403 }
404
405 func EnsureObjectExists(t *testing.T, obj *unstructured.Unstructured, c client.Client) {
406 if err := c.Create(context.Background(), obj); err != nil {
407 if !errors.IsAlreadyExists(err) {
408 t.Errorf("error creating resource %v %v/%v: %v",
409 obj.GetKind(), obj.GetNamespace(), obj.GetName(), err)
410 }
411 }
412 }
413
414 func CRDForGVK(gvk metav1.GroupVersionKind) *apiextensions.CustomResourceDefinition {
415 singular := strings.ToLower(gvk.Kind)
416 plural := text.Pluralize(singular)
417 preserveUnknownFields := true
418 return &apiextensions.CustomResourceDefinition{
419 TypeMeta: metav1.TypeMeta{
420 APIVersion: "apiextensions.k8s.io/v1",
421 Kind: "CustomResourceDefinition",
422 },
423 ObjectMeta: metav1.ObjectMeta{
424 Name: fmt.Sprintf("%v.%v", plural, gvk.Group),
425 },
426 Spec: apiextensions.CustomResourceDefinitionSpec{
427 Group: gvk.Group,
428 Names: apiextensions.CustomResourceDefinitionNames{
429 Plural: plural,
430 Singular: singular,
431 Kind: gvk.Kind,
432 },
433 Scope: apiextensions.NamespaceScoped,
434 Versions: []apiextensions.CustomResourceDefinitionVersion{
435 {
436 Name: gvk.Version,
437 Storage: true,
438 Served: true,
439 Schema: &apiextensions.CustomResourceValidation{
440 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
441 Type: "object",
442 XPreserveUnknownFields: &preserveUnknownFields,
443 },
444 },
445 },
446 },
447 },
448 }
449 }
450
View as plain text