...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/resources.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    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  			// Unique group
    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  // FakeCRDsWithHierarchicalResources returns a CRD list which includes
    79  // hierarchical resources to allow for the testing of resources that reference
    80  // hierarchical resources (e.g. "Project")
    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  // FakeServiceMappingsWithHierarchicalResources returns a ServiceMapping list
   263  // which includes hierarchical resources to allow for the testing of resources
   264  // that reference hierarchical resources (e.g. "Project")
   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