...

Source file src/sigs.k8s.io/kustomize/api/krusty/openapicustomschema_test.go

Documentation: sigs.k8s.io/kustomize/api/krusty

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package krusty_test
     5  
     6  import (
     7  	"os"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  	kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
    13  	"sigs.k8s.io/kustomize/kyaml/openapi"
    14  	"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi"
    15  )
    16  
    17  func writeTestSchema(th kusttest_test.Harness, filepath string) {
    18  	bytes, _ := os.ReadFile("./testdata/customschema.json")
    19  	th.WriteF(filepath+"mycrd_schema.json", string(bytes))
    20  }
    21  
    22  func writeTestSchemaYaml(th kusttest_test.Harness, filepath string) {
    23  	bytes, _ := os.ReadFile("./testdata/customschema.yaml")
    24  	th.WriteF(filepath+"mycrd_schema.yaml", string(bytes))
    25  }
    26  
    27  func writeCustomResource(th kusttest_test.Harness, filepath string) {
    28  	th.WriteF(filepath, `
    29  apiVersion: example.com/v1alpha1
    30  kind: MyCRD
    31  metadata:
    32    name: service
    33  spec:
    34    template:
    35      spec:
    36        containers:
    37        - name: server
    38          image: server
    39          command: example
    40          ports:
    41          - name: grpc
    42            protocol: TCP
    43            containerPort: 8080
    44  `)
    45  }
    46  
    47  func writeOtherCustomResource(th kusttest_test.Harness, filepath string) {
    48  	th.WriteF(filepath, `
    49  apiVersion: v1alpha1
    50  kind: MyCRD
    51  metadata:
    52    name: service
    53  spec:
    54    template:
    55      spec:
    56        containers:
    57        - name: server
    58          image: server
    59          command: example
    60          ports:
    61          - name: grpc
    62            protocol: TCP
    63            containerPort: 8080
    64  `)
    65  }
    66  
    67  func writeTestComponentWithCustomSchema(th kusttest_test.Harness) {
    68  	writeTestSchema(th, "comp/")
    69  	openapi.ResetOpenAPI()
    70  	th.WriteC("comp", `
    71  openapi:
    72    path: mycrd_schema.json
    73  `)
    74  	th.WriteF("comp/stub.yaml", `
    75  apiVersion: v1
    76  kind: Deployment
    77  metadata:
    78    name: stub
    79  spec:
    80    replicas: 1
    81  `)
    82  }
    83  
    84  const customSchemaPatch = `
    85  patchesStrategicMerge:
    86  - |-
    87    apiVersion: example.com/v1alpha1
    88    kind: MyCRD
    89    metadata:
    90      name: service
    91    spec:
    92      template:
    93        spec:
    94          containers:
    95          - name: server
    96            image: nginx
    97  `
    98  
    99  const customSchemaPatchMultipleGvks = `
   100  patchesStrategicMerge:
   101  - |-
   102    apiVersion: example.com/v1alpha1
   103    kind: MyCRD
   104    metadata:
   105      name: service
   106    spec:
   107      template:
   108        spec:
   109          containers:
   110          - name: server
   111            image: nginx
   112  - |-
   113    apiVersion: v1alpha1
   114    kind: MyCRD
   115    metadata:
   116      name: service
   117    spec:
   118      template:
   119        spec:
   120          containers:
   121          - name: server
   122            image: nginx
   123  `
   124  
   125  const patchedCustomResource = `
   126  apiVersion: example.com/v1alpha1
   127  kind: MyCRD
   128  metadata:
   129    name: service
   130  spec:
   131    template:
   132      spec:
   133        containers:
   134        - command: example
   135          image: nginx
   136          name: server
   137          ports:
   138          - containerPort: 8080
   139            name: grpc
   140            protocol: TCP
   141  `
   142  
   143  func runOpenApiTest(t *testing.T, test func(t *testing.T)) {
   144  	t.Helper()
   145  	openapi.ResetOpenAPI()
   146  	test(t)
   147  	openapi.ResetOpenAPI()
   148  }
   149  
   150  // Test for issue #2825
   151  func TestCustomOpenApiFieldBasicUsage(t *testing.T) {
   152  	runOpenApiTest(t, func(t *testing.T) {
   153  		t.Helper()
   154  		th := kusttest_test.MakeHarness(t)
   155  		th.WriteK(".", `
   156  resources:
   157  - mycrd.yaml
   158  openapi:
   159    path: mycrd_schema.json
   160  `+customSchemaPatch)
   161  		writeCustomResource(th, "mycrd.yaml")
   162  		writeTestSchema(th, "./")
   163  		m := th.Run(".", th.MakeDefaultOptions())
   164  		th.AssertActualEqualsExpected(m, patchedCustomResource)
   165  	})
   166  }
   167  
   168  func TestCustomOpenApiFieldBasicUsageWithRemoteSchema(t *testing.T) {
   169  	runOpenApiTest(t, func(t *testing.T) {
   170  		t.Helper()
   171  		th := kusttest_test.MakeHarness(t)
   172  		th.WriteK(".", `
   173  resources:
   174  - mycrd.yaml
   175  openapi:
   176    path: https://github.com/kubernetes-sigs/kustomize/raw/master/api/krusty/testdata/customschema.json
   177  `+customSchemaPatch)
   178  		writeCustomResource(th, "mycrd.yaml")
   179  		writeTestSchema(th, "./")
   180  		m := th.Run(".", th.MakeDefaultOptions())
   181  		th.AssertActualEqualsExpected(m, patchedCustomResource)
   182  	})
   183  }
   184  
   185  func TestCustomOpenApiFieldWithTwoGvks(t *testing.T) {
   186  	runOpenApiTest(t, func(t *testing.T) {
   187  		t.Helper()
   188  		th := kusttest_test.MakeHarness(t)
   189  		th.WriteK(".", `
   190  resources:
   191  - mycrd.yaml
   192  - myothercrd.yaml
   193  openapi:
   194    path: mycrd_schema.json
   195  `+customSchemaPatchMultipleGvks)
   196  		writeCustomResource(th, "mycrd.yaml")
   197  		writeOtherCustomResource(th, "myothercrd.yaml")
   198  		writeTestSchema(th, "./")
   199  		m := th.Run(".", th.MakeDefaultOptions())
   200  		th.AssertActualEqualsExpected(m, `apiVersion: example.com/v1alpha1
   201  kind: MyCRD
   202  metadata:
   203    name: service
   204  spec:
   205    template:
   206      spec:
   207        containers:
   208        - command: example
   209          image: nginx
   210          name: server
   211          ports:
   212          - containerPort: 8080
   213            name: grpc
   214            protocol: TCP
   215  ---
   216  apiVersion: v1alpha1
   217  kind: MyCRD
   218  metadata:
   219    name: service
   220  spec:
   221    template:
   222      spec:
   223        containers:
   224        - command: example
   225          image: nginx
   226          name: server
   227          ports:
   228          - containerPort: 8080
   229            name: grpc
   230            protocol: TCP
   231  `)
   232  	})
   233  }
   234  
   235  func TestCustomOpenApiFieldYaml(t *testing.T) {
   236  	runOpenApiTest(t, func(t *testing.T) {
   237  		t.Helper()
   238  		th := kusttest_test.MakeHarness(t)
   239  		th.WriteK(".", `
   240  resources:
   241  - mycrd.yaml
   242  openapi:
   243    path: mycrd_schema.yaml
   244  `+customSchemaPatch)
   245  		writeCustomResource(th, "mycrd.yaml")
   246  		writeTestSchemaYaml(th, "./")
   247  		m := th.Run(".", th.MakeDefaultOptions())
   248  		th.AssertActualEqualsExpected(m, patchedCustomResource)
   249  	})
   250  }
   251  
   252  // Error if user tries to specify both builtin version
   253  // and custom schema
   254  func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
   255  	runOpenApiTest(t, func(t *testing.T) {
   256  		t.Helper()
   257  		th := kusttest_test.MakeHarness(t)
   258  		th.WriteK(".", `
   259  resources:
   260  - mycrd.yaml
   261  openapi:
   262    version: v1.21.2
   263    path: mycrd_schema.json
   264  `+customSchemaPatch)
   265  		writeCustomResource(th, "mycrd.yaml")
   266  		writeTestSchema(th, "./")
   267  		err := th.RunWithErr(".", th.MakeDefaultOptions())
   268  		require.Error(t, err)
   269  		assert.Equal(t,
   270  			"builtin version and custom schema provided, cannot use both",
   271  			err.Error())
   272  	})
   273  }
   274  
   275  // Test for if the filepath specified is not found
   276  func TestCustomOpenApiFieldFileNotFound(t *testing.T) {
   277  	runOpenApiTest(t, func(t *testing.T) {
   278  		t.Helper()
   279  		th := kusttest_test.MakeHarness(t)
   280  		th.WriteK(".", `
   281  resources:
   282  - mycrd.yaml
   283  openapi:
   284    path: mycrd_schema.json
   285  `+customSchemaPatch)
   286  		writeCustomResource(th, "mycrd.yaml")
   287  		err := th.RunWithErr(".", th.MakeDefaultOptions())
   288  		require.Error(t, err)
   289  		assert.Equal(t,
   290  			"'/mycrd_schema.json' doesn't exist",
   291  			err.Error())
   292  	})
   293  }
   294  
   295  func TestCustomOpenApiFieldFromBase(t *testing.T) {
   296  	runOpenApiTest(t, func(t *testing.T) {
   297  		t.Helper()
   298  		th := kusttest_test.MakeHarness(t)
   299  		th.WriteK("base", `
   300  resources:
   301  - mycrd.yaml
   302  openapi:
   303    path: mycrd_schema.json
   304  `)
   305  		th.WriteK("overlay", `
   306  resources:
   307  - ../base
   308  `+customSchemaPatch)
   309  		writeCustomResource(th, "base/mycrd.yaml")
   310  		writeTestSchema(th, "base/")
   311  		m := th.Run("overlay", th.MakeDefaultOptions())
   312  		th.AssertActualEqualsExpected(m, patchedCustomResource)
   313  		assert.Equal(t, "using custom schema from file provided",
   314  			openapi.GetSchemaVersion())
   315  	})
   316  }
   317  
   318  func TestCustomOpenApiFieldFromBaseWithRemoteSchema(t *testing.T) {
   319  	runOpenApiTest(t, func(t *testing.T) {
   320  		t.Helper()
   321  		th := kusttest_test.MakeHarness(t)
   322  		th.WriteK("base", `
   323  resources:
   324  - mycrd.yaml
   325  openapi:
   326    path: https://github.com/kubernetes-sigs/kustomize/raw/master/api/krusty/testdata/customschema.json
   327  `)
   328  		th.WriteK("overlay", `
   329  resources:
   330  - ../base
   331  `+customSchemaPatch)
   332  		writeCustomResource(th, "base/mycrd.yaml")
   333  		writeTestSchema(th, "base/")
   334  		m := th.Run("overlay", th.MakeDefaultOptions())
   335  		th.AssertActualEqualsExpected(m, patchedCustomResource)
   336  		assert.Equal(t, "using custom schema from file provided",
   337  			openapi.GetSchemaVersion())
   338  	})
   339  }
   340  
   341  func TestCustomOpenApiFieldFromOverlay(t *testing.T) {
   342  	runOpenApiTest(t, func(t *testing.T) {
   343  		t.Helper()
   344  		th := kusttest_test.MakeHarness(t)
   345  		th.WriteK("base", `
   346  resources:
   347  - mycrd.yaml
   348  `)
   349  		th.WriteK("overlay", `
   350  resources:
   351  - ../base
   352  openapi:
   353    path: mycrd_schema.json
   354  `+customSchemaPatch)
   355  		writeCustomResource(th, "base/mycrd.yaml")
   356  		writeTestSchema(th, "overlay/")
   357  		m := th.Run("overlay", th.MakeDefaultOptions())
   358  		th.AssertActualEqualsExpected(m, patchedCustomResource)
   359  		assert.Equal(t, "using custom schema from file provided",
   360  			openapi.GetSchemaVersion())
   361  	})
   362  }
   363  
   364  func TestCustomOpenApiFieldFromOverlayWithRemoteSchema(t *testing.T) {
   365  	runOpenApiTest(t, func(t *testing.T) {
   366  		t.Helper()
   367  		th := kusttest_test.MakeHarness(t)
   368  		th.WriteK("base", `
   369  resources:
   370  - mycrd.yaml
   371  `)
   372  		th.WriteK("overlay", `
   373  resources:
   374  - ../base
   375  openapi:
   376    path: https://github.com/kubernetes-sigs/kustomize/raw/master/api/krusty/testdata/customschema.json
   377  `+customSchemaPatch)
   378  		writeCustomResource(th, "base/mycrd.yaml")
   379  		writeTestSchema(th, "overlay/")
   380  		m := th.Run("overlay", th.MakeDefaultOptions())
   381  		th.AssertActualEqualsExpected(m, patchedCustomResource)
   382  		assert.Equal(t, "using custom schema from file provided",
   383  			openapi.GetSchemaVersion())
   384  	})
   385  }
   386  
   387  func TestCustomOpenApiFieldOverlayTakesPrecedence(t *testing.T) {
   388  	runOpenApiTest(t, func(t *testing.T) {
   389  		t.Helper()
   390  		th := kusttest_test.MakeHarness(t)
   391  		openapi.ResetOpenAPI()
   392  		th.WriteK("base", `
   393  resources:
   394  - mycrd.yaml
   395  openapi:
   396    path: mycrd_schema.json
   397  `)
   398  		th.WriteK("overlay", `
   399  resources:
   400  - ../base
   401  openapi:
   402    version: v1.21.2
   403  `+customSchemaPatch)
   404  		writeCustomResource(th, "base/mycrd.yaml")
   405  		writeTestSchema(th, "base/")
   406  		m := th.Run("overlay", th.MakeDefaultOptions())
   407  		th.AssertActualEqualsExpected(m, `
   408  apiVersion: example.com/v1alpha1
   409  kind: MyCRD
   410  metadata:
   411    name: service
   412  spec:
   413    template:
   414      spec:
   415        containers:
   416        - image: nginx
   417          name: server
   418  `)
   419  		assert.Equal(t, "v1.21.2", openapi.GetSchemaVersion())
   420  	})
   421  }
   422  
   423  func TestCustomOpenApiFieldFromComponent(t *testing.T) {
   424  	runOpenApiTest(t, func(t *testing.T) {
   425  		t.Helper()
   426  		input := []FileGen{
   427  			writeTestBase,
   428  			writeTestComponentWithCustomSchema,
   429  			writeOverlayProd}
   430  
   431  		th := kusttest_test.MakeHarness(t)
   432  		for _, f := range input {
   433  			f(th)
   434  		}
   435  		th.Run("prod", th.MakeDefaultOptions())
   436  		assert.Equal(t, "using custom schema from file provided", openapi.GetSchemaVersion())
   437  	})
   438  }
   439  
   440  // test for https://github.com/kubernetes-sigs/kustomize/issues/4179
   441  // kustomize is not seeing the openapi field from the component defined in the overlay
   442  func TestCustomOpenApiFieldFromComponentWithOverlays(t *testing.T) {
   443  	runOpenApiTest(t, func(t *testing.T) {
   444  		t.Helper()
   445  		th := kusttest_test.MakeHarness(t)
   446  
   447  		// overlay declaring the component
   448  		th.WriteK("overlays/overlay-component-openapi", `resources:
   449  - ../base/
   450  components:
   451  - ../../components/dc-openapi
   452  `)
   453  
   454  		// base kustomization
   455  		th.WriteK("overlays/base", `resources:
   456  - dc.yml
   457  `)
   458  
   459  		// resource declared in the base kustomization
   460  		th.WriteF("overlays/base/dc.yml", `apiVersion: apps.openshift.io/v1
   461  kind: DeploymentConfig
   462  metadata:
   463    name: my-dc
   464  spec:
   465    template:
   466      spec:
   467        initContainers:
   468          - name: init
   469        containers:
   470          - name: container
   471            env:
   472              - name: foo
   473                value: bar
   474            volumeMounts:
   475              - name: cm
   476                mountPath: /opt/cm
   477        volumes:
   478          - name: cm
   479            configMap:
   480              name: cm
   481  `)
   482  
   483  		// openapi schema referred to by the component
   484  		bytes, _ := os.ReadFile("./testdata/openshiftschema.json")
   485  		th.WriteF("components/dc-openapi/openapi.json", string(bytes))
   486  
   487  		// patch referred to by the component
   488  		th.WriteF("components/dc-openapi/patch.yml", `apiVersion: apps.openshift.io/v1
   489  kind: DeploymentConfig
   490  metadata:
   491    name: my-dc
   492  spec:
   493    template:
   494      spec:
   495        containers:
   496          - name: container
   497            volumeMounts:
   498              - name: additional-cm
   499                mountPath: /mnt
   500        volumes:
   501          - name: additional-cm
   502            configMap:
   503               name: additional-cm
   504  `)
   505  
   506  		// component declared in overlay with custom schema and patch
   507  		th.WriteC("components/dc-openapi", `patchesStrategicMerge:
   508    - patch.yml
   509  openapi:
   510    path: openapi.json
   511  `)
   512  
   513  		m := th.Run("overlays/overlay-component-openapi", th.MakeDefaultOptions())
   514  		th.AssertActualEqualsExpected(m, `apiVersion: apps.openshift.io/v1
   515  kind: DeploymentConfig
   516  metadata:
   517    name: my-dc
   518  spec:
   519    template:
   520      spec:
   521        containers:
   522        - env:
   523          - name: foo
   524            value: bar
   525          name: container
   526          volumeMounts:
   527          - mountPath: /mnt
   528            name: additional-cm
   529          - mountPath: /opt/cm
   530            name: cm
   531        initContainers:
   532        - name: init
   533        volumes:
   534        - configMap:
   535            name: additional-cm
   536          name: additional-cm
   537        - configMap:
   538            name: cm
   539          name: cm
   540  `)
   541  	})
   542  }
   543  
   544  func TestCustomOpenApiFieldVersion(t *testing.T) {
   545  	runOpenApiTest(t, func(t *testing.T) {
   546  		t.Helper()
   547  		th := kusttest_test.MakeHarness(t)
   548  		th.WriteK(".", `
   549  openapi:
   550    version: v1.21.2
   551  resources:
   552  - deployment.yaml
   553  `)
   554  		th.WriteF("deployment.yaml", `
   555  apiVersion: apps/v1
   556  kind: Deployment
   557  metadata:
   558    name: myDeployment
   559  spec:
   560    template:
   561      spec:
   562        containers:
   563        - image: whatever
   564  `)
   565  
   566  		m := th.Run(".", th.MakeDefaultOptions())
   567  		th.AssertActualEqualsExpected(m, `
   568  apiVersion: apps/v1
   569  kind: Deployment
   570  metadata:
   571    name: myDeployment
   572  spec:
   573    template:
   574      spec:
   575        containers:
   576        - image: whatever
   577  `)
   578  		assert.Equal(t, "v1.21.2", openapi.GetSchemaVersion())
   579  	})
   580  }
   581  
   582  func TestCustomOpenApiFieldNotBuiltin(t *testing.T) {
   583  	runOpenApiTest(t, func(t *testing.T) {
   584  		t.Helper()
   585  		th := kusttest_test.MakeHarness(t)
   586  		th.WriteK(".", `
   587  openapi:
   588    version: v1.14.1
   589  resources:
   590  - deployment.yaml
   591  `)
   592  		th.WriteF("deployment.yaml", `
   593  apiVersion: apps/v1
   594  kind: Deployment
   595  metadata:
   596    name: myDeployment
   597  spec:
   598    template:
   599      spec:
   600        containers:
   601        - image: whatever
   602  `)
   603  
   604  		err := th.RunWithErr(".", th.MakeDefaultOptions())
   605  		if err == nil {
   606  			t.Fatalf("expected an error")
   607  		}
   608  	})
   609  }
   610  
   611  func TestCustomOpenApiFieldDefaultVersion(t *testing.T) {
   612  	runOpenApiTest(t, func(t *testing.T) {
   613  		t.Helper()
   614  		th := kusttest_test.MakeHarness(t)
   615  		th.WriteK(".", `
   616  resources:
   617  - deployment.yaml
   618  `)
   619  		th.WriteF("deployment.yaml", `
   620  apiVersion: apps/v1
   621  kind: Deployment
   622  metadata:
   623    name: myDeployment
   624  spec:
   625    template:
   626      spec:
   627        containers:
   628        - image: whatever
   629  `)
   630  
   631  		m := th.Run(".", th.MakeDefaultOptions())
   632  		th.AssertActualEqualsExpected(m, `
   633  apiVersion: apps/v1
   634  kind: Deployment
   635  metadata:
   636    name: myDeployment
   637  spec:
   638    template:
   639      spec:
   640        containers:
   641        - image: whatever
   642  `)
   643  		assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion())
   644  	})
   645  }
   646  

View as plain text