...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/crd/crdgeneration/tf2crdgeneration_test.go

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

     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 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