support yaml formatted openapi schema (#4017)

* support yaml formatted openapi schema

* suggested changes
This commit is contained in:
Natasha Sarkar
2021-07-15 14:11:02 -07:00
committed by GitHub
parent 339e33d2f3
commit 0537b59f27
7 changed files with 138 additions and 10 deletions

View File

@@ -17,6 +17,11 @@ func writeTestSchema(th kusttest_test.Harness, filepath string) {
th.WriteF(filepath+"mycrd_schema.json", string(bytes)) th.WriteF(filepath+"mycrd_schema.json", string(bytes))
} }
func writeTestSchemaYaml(th kusttest_test.Harness, filepath string) {
bytes, _ := ioutil.ReadFile("testdata/customschema.yaml")
th.WriteF(filepath+"mycrd_schema.yaml", string(bytes))
}
func writeCustomResource(th kusttest_test.Harness, filepath string) { func writeCustomResource(th kusttest_test.Harness, filepath string) {
th.WriteF(filepath, ` th.WriteF(filepath, `
apiVersion: example.com/v1alpha1 apiVersion: example.com/v1alpha1
@@ -103,6 +108,21 @@ openapi:
th.AssertActualEqualsExpected(m, patchedCustomResource) th.AssertActualEqualsExpected(m, patchedCustomResource)
} }
func TestCustomOpenApiFieldYaml(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- mycrd.yaml
openapi:
path: mycrd_schema.yaml
`+customSchemaPatch)
writeCustomResource(th, "mycrd.yaml")
writeTestSchemaYaml(th, "./")
openapi.ResetOpenAPI()
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, patchedCustomResource)
}
// Error if user tries to specify both builtin version // Error if user tries to specify both builtin version
// and custom schema // and custom schema
func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) { func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {

75
api/krusty/testdata/customschema.yaml vendored Normal file
View File

@@ -0,0 +1,75 @@
definitions:
v1alpha1.MyCRD:
properties:
apiVersion:
type: string
kind:
type: string
metadata:
type: object
spec:
properties:
template:
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
type: object
status:
properties:
success:
type: boolean
type: object
type: object
x-kubernetes-group-version-kind:
- group: example.com
kind: MyCRD
version: v1alpha1
io.k8s.api.core.v1.PodTemplateSpec:
properties:
metadata:
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
spec:
"$ref": "#/definitions/io.k8s.api.core.v1.PodSpec"
type: object
io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta:
properties:
name:
type: string
type: object
io.k8s.api.core.v1.PodSpec:
properties:
containers:
items:
"$ref": "#/definitions/io.k8s.api.core.v1.Container"
type: array
x-kubernetes-patch-merge-key: name
x-kubernetes-patch-strategy: merge
type: object
io.k8s.api.core.v1.Container:
properties:
command:
items:
type: string
type: array
image:
type: string
name:
type: string
ports:
items:
"$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort"
type: array
x-kubernetes-list-map-keys:
- containerPort
- protocol
x-kubernetes-list-type: map
x-kubernetes-patch-merge-key: containerPort
x-kubernetes-patch-strategy: merge
type: object
io.k8s.api.core.v1.ContainerPort:
properties:
containerPort:
type: integer
name:
type: string
protocol:
type: string
type: object

View File

@@ -248,4 +248,5 @@ k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2R
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA= sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -8,26 +8,38 @@ import (
"os/exec" "os/exec"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/yaml"
) )
var format string
// NewCmdFetch makes a new fetch command. // NewCmdFetch makes a new fetch command.
func NewCmdFetch(w io.Writer) *cobra.Command { func NewCmdFetch(w io.Writer) *cobra.Command {
infoCmd := cobra.Command{ fetchCmd := cobra.Command{
Use: "fetch", Use: "fetch",
Short: `Fetches the OpenAPI specification from the current kubernetes cluster specified Short: `Fetches the OpenAPI specification from the current kubernetes cluster specified
in the user's kubeconfig`, in the user's kubeconfig`,
Example: `kustomize openapi fetch`, Example: `kustomize openapi fetch`,
Run: func(cmd *cobra.Command, args []string) { RunE: func(cmd *cobra.Command, args []string) error {
printSchema(w) return printSchema(w)
}, },
} }
return &infoCmd fetchCmd.Flags().StringVar(
&format,
"format",
"json",
"Specify format for fetched schema ('json' or 'yaml')")
return &fetchCmd
} }
func printSchema(w io.Writer) { func printSchema(w io.Writer) error {
if format != "json" && format != "yaml" {
return fmt.Errorf("format must be either 'json' or 'yaml'")
}
errMsg := ` errMsg := `
Error fetching schema from cluster. Error fetching schema from cluster.
Please make sure kubectl is installed and its context is set correctly. Please make sure kubectl is installed, its context is set correctly, and your cluster is up.
Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/install-kubectl/` Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/install-kubectl/`
command := exec.Command("kubectl", []string{"get", "--raw", "/openapi/v2"}...) command := exec.Command("kubectl", []string{"get", "--raw", "/openapi/v2"}...)
@@ -36,9 +48,10 @@ Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/inst
command.Stdout = &stdout command.Stdout = &stdout
command.Stderr = &stderr command.Stderr = &stderr
err := command.Run() err := command.Run()
if err != nil || stdout.String() == "" { if err != nil {
fmt.Fprintln(w, err, stderr.String()+errMsg) return fmt.Errorf("%w\n%s", err, stderr.String()+errMsg)
return } else if stdout.String() == "" {
return fmt.Errorf(stderr.String() + errMsg)
} }
// format and output // format and output
@@ -46,5 +59,14 @@ Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/inst
output := stdout.Bytes() output := stdout.Bytes()
json.Unmarshal(output, &jsonSchema) json.Unmarshal(output, &jsonSchema)
output, _ = json.MarshalIndent(jsonSchema, "", " ") output, _ = json.MarshalIndent(jsonSchema, "", " ")
if format == "yaml" {
output, err = yaml.JSONToYAML(output)
if err != nil {
return err
}
}
fmt.Fprintln(w, string(output)) fmt.Fprintln(w, string(output))
return nil
} }

View File

@@ -19,6 +19,7 @@ require (
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
sigs.k8s.io/yaml v1.2.0
) )
// These can be removed after upgrading golangci-lint (Issue #3663) // These can be removed after upgrading golangci-lint (Issue #3663)

View File

@@ -225,4 +225,5 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -16,6 +16,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi" "sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi"
"sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi" "sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
k8syaml "sigs.k8s.io/yaml"
) )
// globalSchema contains global state information about the openapi // globalSchema contains global state information about the openapi
@@ -536,7 +537,14 @@ func parseBuiltinSchema(version string) {
// parse parses and indexes a single json schema // parse parses and indexes a single json schema
func parse(b []byte) error { func parse(b []byte) error {
var swagger spec.Swagger var swagger spec.Swagger
s := string(b)
if len(s) > 0 && s[0] != '{' {
var err error
b, err = k8syaml.YAMLToJSON(b)
if err != nil {
return errors.Wrap(err)
}
}
if err := swagger.UnmarshalJSON(b); err != nil { if err := swagger.UnmarshalJSON(b); err != nil {
return errors.Wrap(err) return errors.Wrap(err)
} }