mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-29 09:40:49 +00:00
Merge pull request #3245 from natasha41575/UseOpenApiVersions
Use `openApi` field in kustomization file to specify OpenAPI schema version
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
301
api/krusty/openapiversion_test.go
Normal file
301
api/krusty/openapiversion_test.go
Normal file
@@ -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())
|
||||
}
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
const Info = "{title:Kubernetes,version:v1.18.4}\n{title:Kubernetes,version:v1.18.6}\n{title:Kubernetes,version:v1.18.8}\n{title:Kubernetes,version:v1.19.0}\n{title:Kubernetes,version:v1.19.1}"
|
||||
|
||||
var OpenApiMustAsset = map[string]func(string) []byte{
|
||||
var OpenAPIMustAsset = map[string]func(string) []byte{
|
||||
"v1184": v1184.MustAsset,
|
||||
"v1186": v1186.MustAsset,
|
||||
"v1188": v1188.MustAsset,
|
||||
@@ -23,4 +23,4 @@ var OpenApiMustAsset = map[string]func(string) []byte{
|
||||
"v1191": v1191.MustAsset,
|
||||
}
|
||||
|
||||
const DefaultOpenApi = "v1191"
|
||||
const DefaultOpenAPI = "v1191"
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
@@ -22,14 +21,30 @@ import (
|
||||
// globalSchema contains global state information about the openapi
|
||||
var globalSchema openapiData
|
||||
|
||||
// kubernetesOpenAPIVersion specifies which builtin kubernetes schema to use
|
||||
var kubernetesOpenAPIVersion string
|
||||
|
||||
// openapiData contains the parsed openapi state. this is in a struct rather than
|
||||
// a list of vars so that it can be reset from tests.
|
||||
type openapiData struct {
|
||||
setup sync.Once
|
||||
schema spec.Schema
|
||||
schemaByResourceType map[yaml.TypeMeta]*spec.Schema
|
||||
// schema holds the OpenAPI schema data
|
||||
schema spec.Schema
|
||||
|
||||
// schemaForResourceType is a map of Resource types to their schemas
|
||||
schemaByResourceType map[yaml.TypeMeta]*spec.Schema
|
||||
|
||||
// namespaceabilityByResourceType stores whether a given Resource type
|
||||
// is namespaceable or not
|
||||
namespaceabilityByResourceType map[yaml.TypeMeta]bool
|
||||
noUseBuiltInSchema bool
|
||||
|
||||
// noUseBuiltInSchema stores whether we want to prevent using the built-n
|
||||
// Kubernetes schema as part of the global schema
|
||||
noUseBuiltInSchema bool
|
||||
|
||||
// currentOpenAPIVersion stores the version if the kubernetes openapi data
|
||||
// that is currently stored as the schema, so that we only reparse the
|
||||
// schema when necessary (to speed up performance)
|
||||
currentOpenAPIVersion string
|
||||
}
|
||||
|
||||
// ResourceSchema wraps the OpenAPI Schema.
|
||||
@@ -387,9 +402,9 @@ func (rs *ResourceSchema) PatchStrategyAndKey() (string, string) {
|
||||
}
|
||||
|
||||
const (
|
||||
// kubernetesAPIDefaultVersion is the latest version number of the statically compiled in
|
||||
// kubernetesOpenAPIDefaultVersion is the latest version number of the statically compiled in
|
||||
// OpenAPI schema for kubernetes built-in types
|
||||
kubernetesAPIDefaultVersion = kubernetesapi.DefaultOpenApi
|
||||
kubernetesOpenAPIDefaultVersion = kubernetesapi.DefaultOpenAPI
|
||||
|
||||
// kustomizationAPIAssetName is the name of the asset containing the statically compiled in
|
||||
// OpenAPI definitions for Kustomization built-in types
|
||||
@@ -419,29 +434,65 @@ const (
|
||||
kindKey = "kind"
|
||||
)
|
||||
|
||||
// SetSchemaVersion sets the kubernetes OpenAPI schema version to use
|
||||
func SetSchemaVersion(openAPIField map[string]string, reset bool) error {
|
||||
// this should only be set once
|
||||
if kubernetesOpenAPIVersion != "" && !reset {
|
||||
return nil
|
||||
}
|
||||
|
||||
kubernetesOpenAPIVersion = strings.ReplaceAll(openAPIField["version"], ".", "")
|
||||
if kubernetesOpenAPIVersion == "" {
|
||||
return nil
|
||||
}
|
||||
if _, ok := kubernetesapi.OpenAPIMustAsset[kubernetesOpenAPIVersion]; !ok {
|
||||
return fmt.Errorf("the specified OpenAPI version is not built in")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSchemaVersion returns what kubernetes OpenAPI version is being used
|
||||
func GetSchemaVersion() string {
|
||||
if kubernetesOpenAPIVersion == "" {
|
||||
return kubernetesOpenAPIDefaultVersion
|
||||
}
|
||||
return kubernetesOpenAPIVersion
|
||||
}
|
||||
|
||||
// initSchema parses the json schema
|
||||
func initSchema() {
|
||||
globalSchema.setup.Do(func() {
|
||||
if globalSchema.noUseBuiltInSchema {
|
||||
// don't parse the built in schema
|
||||
return
|
||||
}
|
||||
currentVersion := kubernetesOpenAPIVersion
|
||||
if currentVersion == "" {
|
||||
currentVersion = kubernetesOpenAPIDefaultVersion
|
||||
}
|
||||
if globalSchema.currentOpenAPIVersion != currentVersion {
|
||||
parseSchema(currentVersion)
|
||||
}
|
||||
globalSchema.currentOpenAPIVersion = currentVersion
|
||||
}
|
||||
|
||||
// parse the swagger, this should never fail
|
||||
assetName := filepath.Join(
|
||||
"kubernetesapi",
|
||||
kubernetesAPIDefaultVersion,
|
||||
"swagger.json")
|
||||
if err := parse(kubernetesapi.OpenApiMustAsset[kubernetesAPIDefaultVersion](assetName)); err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
}
|
||||
// parseSchema calls parse to parse the json schemas
|
||||
func parseSchema(version string) {
|
||||
if globalSchema.noUseBuiltInSchema {
|
||||
// don't parse the built in schema
|
||||
return
|
||||
}
|
||||
|
||||
if err := parse(kustomizationapi.MustAsset(kustomizationAPIAssetName)); err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
// parse the swagger, this should never fail
|
||||
assetName := filepath.Join(
|
||||
"kubernetesapi",
|
||||
version,
|
||||
"swagger.json")
|
||||
|
||||
if err := parse(kubernetesapi.OpenAPIMustAsset[version](assetName)); err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := parse(kustomizationapi.MustAsset(kustomizationAPIAssetName)); err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// parse parses and indexes a single json schema
|
||||
|
||||
@@ -62,7 +62,7 @@ EOF
|
||||
# add map for `initSchema` in openapi.go to use
|
||||
cat <<EOF >>kubernetesapi/openapiinfo.go
|
||||
|
||||
var OpenApiMustAsset = map[string]func(string)[]byte{
|
||||
var OpenAPIMustAsset = map[string]func(string)[]byte{
|
||||
EOF
|
||||
|
||||
latest=""
|
||||
@@ -78,7 +78,7 @@ done
|
||||
cat <<EOF >>kubernetesapi/openapiinfo.go
|
||||
}
|
||||
|
||||
const DefaultOpenApi = "$latest"
|
||||
const DefaultOpenAPI = "$latest"
|
||||
EOF
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user