mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
add openapi/path field to use custom openapi schema document
This commit is contained in:
@@ -6,6 +6,7 @@ package target
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -377,10 +378,17 @@ 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)
|
||||
var bytes []byte
|
||||
path := ldr.Root()
|
||||
if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists {
|
||||
bytes, err = ldr.Load(filepath.Join(path, openApiPath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = openapi.SetSchema(subKt.Kustomization().OpenAPI, bytes, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err, "couldn't set openapi version for path '%s'", ldr.Root())
|
||||
return nil, err
|
||||
}
|
||||
if isComponent && subKt.kustomization.Kind != types.ComponentKind {
|
||||
return nil, fmt.Errorf(
|
||||
|
||||
@@ -5,6 +5,7 @@ package krusty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/builtins"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
@@ -73,7 +74,14 @@ func (b *Kustomizer) Run(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = openapi.SetSchemaVersion(kt.Kustomization().OpenAPI, true)
|
||||
var bytes []byte
|
||||
if openApiPath, exists := kt.Kustomization().OpenAPI["path"]; exists {
|
||||
bytes, err = ldr.Load(filepath.Join(ldr.Root(), openApiPath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = openapi.SetSchema(kt.Kustomization().OpenAPI, bytes, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
403
api/krusty/openapicustomschema_test.go
Normal file
403
api/krusty/openapicustomschema_test.go
Normal file
@@ -0,0 +1,403 @@
|
||||
package krusty_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func writeTestSchema(th kusttest_test.Harness, filepath string) {
|
||||
bytes, _ := ioutil.ReadFile("testdata/customschema.json")
|
||||
th.WriteF(filepath+"mycrd_schema.json", string(bytes))
|
||||
}
|
||||
|
||||
func writeTestComponentWithCustomSchema(th kusttest_test.Harness) {
|
||||
writeTestSchema(th, "/app/comp/")
|
||||
openapi.ResetOpenAPI()
|
||||
th.WriteC("/app/comp", `
|
||||
openapi:
|
||||
path: mycrd_schema.json
|
||||
`)
|
||||
th.WriteF("/app/comp/stub.yaml", `
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: stub
|
||||
spec:
|
||||
replicas: 1
|
||||
`)
|
||||
}
|
||||
|
||||
// Test for issue #2825
|
||||
func TestCustomOpenApiFieldBasicUsage(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("/app", `
|
||||
resources:
|
||||
- mycrd.yaml
|
||||
|
||||
openapi:
|
||||
path: mycrd_schema.json
|
||||
|
||||
patchesStrategicMerge:
|
||||
- |-
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: nginx
|
||||
`)
|
||||
th.WriteF("/app/mycrd.yaml", `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: server
|
||||
command: example
|
||||
ports:
|
||||
- name: grpc
|
||||
protocol: TCP
|
||||
containerPort: 8080
|
||||
`)
|
||||
writeTestSchema(th, "/app/")
|
||||
openapi.ResetOpenAPI()
|
||||
|
||||
m := th.Run("/app", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- command: example
|
||||
image: nginx
|
||||
name: server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: grpc
|
||||
protocol: TCP
|
||||
`)
|
||||
}
|
||||
|
||||
// Error if user tries to specify both builtin version
|
||||
// and custom schema
|
||||
func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("/app", `
|
||||
resources:
|
||||
- mycrd.yaml
|
||||
|
||||
openapi:
|
||||
version: v1.18.8
|
||||
path: mycrd_schema.json
|
||||
|
||||
patchesStrategicMerge:
|
||||
- |-
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: nginx
|
||||
`)
|
||||
th.WriteF("/app/mycrd.yaml", `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: server
|
||||
command: example
|
||||
ports:
|
||||
- name: grpc
|
||||
protocol: TCP
|
||||
containerPort: 8080
|
||||
`)
|
||||
writeTestSchema(th, "/app/")
|
||||
openapi.ResetOpenAPI()
|
||||
err := th.RunWithErr("/app", th.MakeDefaultOptions())
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t,
|
||||
"builtin version and custom schema provided, cannot use both",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
// Test for if the filepath specified is not found
|
||||
func TestCustomOpenApiFieldFileNotFound(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("/app", `
|
||||
resources:
|
||||
- mycrd.yaml
|
||||
|
||||
openapi:
|
||||
path: mycrd_schema.json
|
||||
|
||||
patchesStrategicMerge:
|
||||
- |-
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: nginx
|
||||
`)
|
||||
th.WriteF("/app/mycrd.yaml", `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: server
|
||||
command: example
|
||||
ports:
|
||||
- name: grpc
|
||||
protocol: TCP
|
||||
containerPort: 8080
|
||||
`)
|
||||
openapi.ResetOpenAPI()
|
||||
err := th.RunWithErr("/app", th.MakeDefaultOptions())
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t,
|
||||
"'/app/mycrd_schema.json' doesn't exist",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
func TestCustomOpenApiFieldFromBase(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("base", `
|
||||
resources:
|
||||
- mycrd.yaml
|
||||
|
||||
openapi:
|
||||
path: mycrd_schema.json
|
||||
`)
|
||||
th.WriteF("base/mycrd.yaml", `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: server
|
||||
command: example
|
||||
ports:
|
||||
- name: grpc
|
||||
protocol: TCP
|
||||
containerPort: 8080
|
||||
`)
|
||||
th.WriteK("overlay", `
|
||||
resources:
|
||||
- ../base
|
||||
|
||||
patchesStrategicMerge:
|
||||
- |-
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: nginx
|
||||
`)
|
||||
writeTestSchema(th, "/base/")
|
||||
openapi.ResetOpenAPI()
|
||||
m := th.Run("overlay", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- command: example
|
||||
image: nginx
|
||||
name: server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: grpc
|
||||
protocol: TCP
|
||||
`)
|
||||
assert.Equal(t, "using custom schema from file provided",
|
||||
openapi.GetSchemaVersion())
|
||||
}
|
||||
|
||||
func TestCustomOpenApiFieldFromOverlay(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("base", `
|
||||
resources:
|
||||
- mycrd.yaml
|
||||
`)
|
||||
th.WriteF("base/mycrd.yaml", `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: server
|
||||
command: example
|
||||
ports:
|
||||
- name: grpc
|
||||
protocol: TCP
|
||||
containerPort: 8080
|
||||
`)
|
||||
th.WriteK("overlay", `
|
||||
resources:
|
||||
- ../base
|
||||
openapi:
|
||||
path: mycrd_schema.json
|
||||
patchesStrategicMerge:
|
||||
- |-
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: nginx
|
||||
`)
|
||||
writeTestSchema(th, "/overlay/")
|
||||
openapi.ResetOpenAPI()
|
||||
m := th.Run("overlay", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- command: example
|
||||
image: nginx
|
||||
name: server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: grpc
|
||||
protocol: TCP
|
||||
`)
|
||||
assert.Equal(t, "using custom schema from file provided",
|
||||
openapi.GetSchemaVersion())
|
||||
}
|
||||
|
||||
func TestCustomOpenApiFieldOverlayTakesPrecedence(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("base", `
|
||||
resources:
|
||||
- mycrd.yaml
|
||||
openapi:
|
||||
path: mycrd_schema.json
|
||||
`)
|
||||
th.WriteF("base/mycrd.yaml", `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: server
|
||||
command: example
|
||||
ports:
|
||||
- name: grpc
|
||||
protocol: TCP
|
||||
containerPort: 8080
|
||||
`)
|
||||
th.WriteK("overlay", `
|
||||
resources:
|
||||
- ../base
|
||||
openapi:
|
||||
version: v1.19.1
|
||||
patchesStrategicMerge:
|
||||
- |-
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: nginx
|
||||
`)
|
||||
writeTestSchema(th, "/base/")
|
||||
openapi.ResetOpenAPI()
|
||||
m := th.Run("overlay", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: server
|
||||
`)
|
||||
assert.Equal(t, "v1191",
|
||||
openapi.GetSchemaVersion())
|
||||
}
|
||||
|
||||
func TestCustomOpenAPIFieldFromComponent(t *testing.T) {
|
||||
input := []FileGen{
|
||||
writeTestBase,
|
||||
writeTestComponentWithCustomSchema,
|
||||
writeOverlayProd}
|
||||
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
for _, f := range input {
|
||||
f(th)
|
||||
}
|
||||
openapi.ResetOpenAPI()
|
||||
th.Run(runPath, th.MakeDefaultOptions())
|
||||
assert.Equal(t, "using custom schema from file provided", openapi.GetSchemaVersion())
|
||||
}
|
||||
@@ -285,12 +285,13 @@ spec:
|
||||
`)
|
||||
}
|
||||
|
||||
const runPath = "/app/prod"
|
||||
|
||||
func TestOpenAPIFieldFromComponent(t *testing.T) {
|
||||
input := []FileGen{
|
||||
writeTestBase,
|
||||
writeTestComponentWithOlderOpenAPIVersion,
|
||||
writeOverlayProd}
|
||||
runPath := "/app/prod"
|
||||
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
for _, f := range input {
|
||||
|
||||
92532
api/krusty/testdata/customschema.json
vendored
Normal file
92532
api/krusty/testdata/customschema.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,9 @@ var globalSchema openapiData
|
||||
// kubernetesOpenAPIVersion specifies which builtin kubernetes schema to use
|
||||
var kubernetesOpenAPIVersion string
|
||||
|
||||
// customSchemaFile stores the custom OpenApi schema if it is provided
|
||||
var customSchema []byte
|
||||
|
||||
// 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 {
|
||||
@@ -363,25 +366,20 @@ func (rs *ResourceSchema) PatchStrategyAndKeyList() (string, []string) {
|
||||
// empty patch strategy
|
||||
return "", []string{}
|
||||
}
|
||||
|
||||
mkList, found := rs.Schema.Extensions[kubernetesMergeKeyMapList]
|
||||
if found {
|
||||
// mkList is []interface, convert to []string
|
||||
mkListStr := make([]string, len(mkList.([]interface{})))
|
||||
|
||||
for i, v := range mkList.([]interface{}) {
|
||||
mkListStr[i] = v.(string)
|
||||
}
|
||||
|
||||
return ps.(string), mkListStr
|
||||
}
|
||||
|
||||
mk, found := rs.Schema.Extensions[kubernetesMergeKeyExtensionKey]
|
||||
if !found {
|
||||
// no mergeKey -- may be a primitive associative list (e.g. finalizers)
|
||||
return ps.(string), []string{}
|
||||
}
|
||||
|
||||
return ps.(string), []string{mk.(string)}
|
||||
}
|
||||
|
||||
@@ -434,45 +432,72 @@ const (
|
||||
kindKey = "kind"
|
||||
)
|
||||
|
||||
// SetSchemaVersion sets the kubernetes OpenAPI schema version to use
|
||||
func SetSchemaVersion(openAPIField map[string]string, reset bool) error {
|
||||
// SetSchema sets the kubernetes OpenAPI schema version to use
|
||||
func SetSchema(openAPIField map[string]string, schema []byte, reset bool) error {
|
||||
// this should only be set once
|
||||
if kubernetesOpenAPIVersion != "" && !reset {
|
||||
schemaIsSet := (kubernetesOpenAPIVersion != "") || customSchema != nil
|
||||
if schemaIsSet && !reset {
|
||||
return nil
|
||||
}
|
||||
|
||||
kubernetesOpenAPIVersion = strings.ReplaceAll(openAPIField["version"], ".", "")
|
||||
version, exists := openAPIField["version"]
|
||||
if exists && schema != nil {
|
||||
return fmt.Errorf("builtin version and custom schema provided, cannot use both")
|
||||
}
|
||||
|
||||
if schema != nil { // use custom schema
|
||||
customSchema = schema
|
||||
kubernetesOpenAPIVersion = "custom"
|
||||
return nil
|
||||
}
|
||||
|
||||
// use builtin version
|
||||
kubernetesOpenAPIVersion = strings.ReplaceAll(version, ".", "")
|
||||
if kubernetesOpenAPIVersion == "" {
|
||||
return nil
|
||||
}
|
||||
if _, ok := kubernetesapi.OpenAPIMustAsset[kubernetesOpenAPIVersion]; !ok {
|
||||
return fmt.Errorf("the specified OpenAPI version is not built in")
|
||||
}
|
||||
customSchema = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSchemaVersion returns what kubernetes OpenAPI version is being used
|
||||
func GetSchemaVersion() string {
|
||||
if kubernetesOpenAPIVersion == "" {
|
||||
switch {
|
||||
case kubernetesOpenAPIVersion == "" && customSchema == nil:
|
||||
return kubernetesOpenAPIDefaultVersion
|
||||
case customSchema != nil:
|
||||
return "using custom schema from file provided"
|
||||
default:
|
||||
return kubernetesOpenAPIVersion
|
||||
}
|
||||
return kubernetesOpenAPIVersion
|
||||
}
|
||||
|
||||
// initSchema parses the json schema
|
||||
func initSchema() {
|
||||
if customSchema != nil {
|
||||
ResetOpenAPI()
|
||||
err := parse(customSchema)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
currentVersion := kubernetesOpenAPIVersion
|
||||
if currentVersion == "" {
|
||||
currentVersion = kubernetesOpenAPIDefaultVersion
|
||||
}
|
||||
if globalSchema.currentOpenAPIVersion != currentVersion {
|
||||
parseSchema(currentVersion)
|
||||
parseBuiltinSchema(currentVersion)
|
||||
}
|
||||
globalSchema.currentOpenAPIVersion = currentVersion
|
||||
}
|
||||
|
||||
// parseSchema calls parse to parse the json schemas
|
||||
func parseSchema(version string) {
|
||||
// parseBuiltinSchema calls parse to parse the json schemas
|
||||
func parseBuiltinSchema(version string) {
|
||||
if globalSchema.noUseBuiltInSchema {
|
||||
// don't parse the built in schema
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user