diff --git a/api/internal/target/kusttarget.go b/api/internal/target/kusttarget.go index b05743e6f..3f6c40ef3 100644 --- a/api/internal/target/kusttarget.go +++ b/api/internal/target/kusttarget.go @@ -19,6 +19,7 @@ import ( "sigs.k8s.io/kustomize/api/konfig" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/yaml" ) @@ -382,6 +383,11 @@ func (kt *KustTarget) accumulateDirectory( return nil, errors.Wrapf( err, "couldn't make target for path '%s'", ldr.Root()) } + err = openapi.SetSchemaVersion(subKt.Kustomization().OpenAPI, false) + if err != nil { + return nil, errors.Wrapf( + err, "couldn't set openapi version for path '%s'", ldr.Root()) + } if isComponent && subKt.kustomization.Kind != types.ComponentKind { return nil, fmt.Errorf( "expected kind '%s' for path '%s' but got '%s'", types.ComponentKind, ldr.Root(), subKt.kustomization.Kind) diff --git a/api/krusty/kustomizer.go b/api/krusty/kustomizer.go index 3995b7f01..3c52661cf 100644 --- a/api/krusty/kustomizer.go +++ b/api/krusty/kustomizer.go @@ -16,6 +16,7 @@ import ( "sigs.k8s.io/kustomize/api/provider" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/openapi" ) // Kustomizer performs kustomizations. It's meant to behave @@ -73,6 +74,10 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) { if err != nil { return nil, err } + err = openapi.SetSchemaVersion(kt.Kustomization().OpenAPI, true) + if err != nil { + return nil, err + } var m resmap.ResMap m, err = kt.MakeCustomizedResMap() if err != nil { diff --git a/api/krusty/openapiversion_test.go b/api/krusty/openapiversion_test.go new file mode 100644 index 000000000..0732c783c --- /dev/null +++ b/api/krusty/openapiversion_test.go @@ -0,0 +1,301 @@ +package krusty_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/kyaml/openapi" + "sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi" +) + +func TestOpenApiFieldBasicUsage(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("/app", ` +openapi: + version: v1.18.8 +resources: +- deployment.yaml +`) + th.WriteF("/app/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + + m := th.Run("/app", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + assert.Equal(t, "v1188", openapi.GetSchemaVersion()) +} + +func TestOpenApiFieldNotBuiltin(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("/app", ` +openapi: + version: v1.14.1 +resources: +- deployment.yaml +`) + th.WriteF("/app/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + + err := th.RunWithErr("/app", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("expected an error") + } +} + +func TestOpenApiFieldDefaultVersion(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("/app", ` +resources: +- deployment.yaml +`) + th.WriteF("/app/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + + m := th.Run("/app", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion()) +} + +func TestOpenApiFieldFromBase(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +openapi: + version: v1.19.0 +namePrefix: a- +resources: +- deployment.yaml +`) + th.WriteF("base/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + th.WriteK("overlay", ` +namePrefix: b- +resources: +- ../base +patchesStrategicMerge: +- depPatch.yaml +`) + th.WriteF("overlay/depPatch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + replicas: 999 +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: b-a-myDeployment +spec: + replicas: 999 + template: + spec: + containers: + - image: whatever +`) + assert.Equal(t, "v1190", openapi.GetSchemaVersion()) +} + +func TestOpenApiFieldFromOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +namePrefix: a- +resources: +- deployment.yaml +`) + th.WriteF("base/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + th.WriteK("overlay", ` +openapi: + version: v1.18.8 +namePrefix: b- +resources: +- ../base +patchesStrategicMerge: +- depPatch.yaml +`) + th.WriteF("overlay/depPatch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + replicas: 999 +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: b-a-myDeployment +spec: + replicas: 999 + template: + spec: + containers: + - image: whatever +`) + assert.Equal(t, "v1188", openapi.GetSchemaVersion()) +} + +func TestOpenApiFieldOverlayTakesPrecedence(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +openapi: + version: v1.19.0 +namePrefix: a- +resources: +- deployment.yaml +`) + th.WriteF("base/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + th.WriteK("overlay", ` +openapi: + version: v1.18.8 +namePrefix: b- +resources: +- ../base +patchesStrategicMerge: +- depPatch.yaml +`) + th.WriteF("overlay/depPatch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + replicas: 999 +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: b-a-myDeployment +spec: + replicas: 999 + template: + spec: + containers: + - image: whatever +`) + assert.Equal(t, "v1188", openapi.GetSchemaVersion()) +} + +func TestOpenAPIFieldFromComponentDefault(t *testing.T) { + input := []FileGen{writeTestBase, writeTestComponent, writeOverlayProd} + runPath := "/app/prod" + + th := kusttest_test.MakeHarness(t) + for _, f := range input { + f(th) + } + th.Run(runPath, th.MakeDefaultOptions()) + assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion()) +} + +func writeTestComponentWithOlderOpenAPIVersion(th kusttest_test.Harness) { + th.WriteC("/app/comp", ` +openapi: + version: v1.18.8 +`) + th.WriteF("/app/comp/stub.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: stub +spec: + replicas: 1 +`) +} + +func TestOpenAPIFieldFromComponent(t *testing.T) { + input := []FileGen{ + writeTestBase, + writeTestComponentWithOlderOpenAPIVersion, + writeOverlayProd} + runPath := "/app/prod" + + th := kusttest_test.MakeHarness(t) + for _, f := range input { + f(th) + } + th.Run(runPath, th.MakeDefaultOptions()) + assert.Equal(t, "v1188", openapi.GetSchemaVersion()) +} diff --git a/api/types/kustomization.go b/api/types/kustomization.go index b290da35c..dfb198ed3 100644 --- a/api/types/kustomization.go +++ b/api/types/kustomization.go @@ -25,6 +25,9 @@ type Kustomization struct { // MetaData is a pointer to avoid marshalling empty struct MetaData *ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + // OpenAPI contains information about what kubernetes schema to use. + OpenAPI map[string]string `json:"openapi,omitempty" yaml:"openapi,omitempty"` + // // Operators - what kustomize can do. //