Validate against OpenAPI schema (if available)
This commit is contained in:
parent
0ade03bc0f
commit
b7b5457050
@ -187,7 +187,7 @@ func parsePruneResources(mapper meta.RESTMapper, gvks []string) ([]pruneResource
|
||||
}
|
||||
|
||||
func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, options *ApplyOptions) error {
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C
|
||||
// build the builder
|
||||
o.builder = f.NewBuilder(!o.local)
|
||||
if !o.local {
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt
|
||||
if options.EditBeforeCreate {
|
||||
return RunEditOnCreate(f, out, errOut, cmd, &options.FilenameOptions)
|
||||
}
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func NewCmdReplace(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -171,7 +171,7 @@ func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
|
||||
}
|
||||
|
||||
func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *resource.FilenameOptions) error {
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args
|
||||
mapper, typer := f.Object()
|
||||
|
||||
if len(filename) != 0 {
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ func (f *FakeFactory) ResolveImage(name string) (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (f *FakeFactory) Validator(validate bool, cacheDir string) (validation.Schema, error) {
|
||||
func (f *FakeFactory) Validator(validate bool, openapi bool, cacheDir string) (validation.Schema, error) {
|
||||
return f.tf.Validator, f.tf.Err
|
||||
}
|
||||
|
||||
@ -699,7 +699,7 @@ func (f *fakeAPIFactory) AttachablePodForObject(object runtime.Object, timeout t
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeAPIFactory) Validator(validate bool, cacheDir string) (validation.Schema, error) {
|
||||
func (f *fakeAPIFactory) Validator(validate bool, openapi bool, cacheDir string) (validation.Schema, error) {
|
||||
return f.tf.Validator, f.tf.Err
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ go_library(
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/kubectl:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi/validation:go_default_library",
|
||||
"//pkg/kubectl/plugins:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//pkg/kubectl/validation:go_default_library",
|
||||
|
@ -229,7 +229,7 @@ func (o *EditOptions) Run() error {
|
||||
glog.V(4).Infof("User edited:\n%s", string(edited))
|
||||
|
||||
// Apply validation
|
||||
schema, err := o.f.Validator(o.EnableValidation, o.SchemaCacheDir)
|
||||
schema, err := o.f.Validator(o.EnableValidation, o.UseOpenAPI, o.SchemaCacheDir)
|
||||
if err != nil {
|
||||
return preservedFile(err, file, o.ErrOut)
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ type ObjectMappingFactory interface {
|
||||
AttachablePodForObject(object runtime.Object, timeout time.Duration) (*api.Pod, error)
|
||||
|
||||
// Returns a schema that can validate objects stored on disk.
|
||||
Validator(validate bool, cacheDir string) (validation.Schema, error)
|
||||
Validator(validate bool, openapi bool, cacheDir string) (validation.Schema, error)
|
||||
// SwaggerSchema returns the schema declaration for the provided group version kind.
|
||||
SwaggerSchema(schema.GroupVersionKind) (*swagger.ApiDeclaration, error)
|
||||
// OpenAPISchema returns the schema openapi schema definiton
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"time"
|
||||
|
||||
swagger "github.com/emicklei/go-restful-swagger12"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
@ -47,6 +48,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
openapivalidation "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/validation"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/validation"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
@ -402,8 +404,20 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout tim
|
||||
return pod, err
|
||||
}
|
||||
|
||||
func (f *ring1Factory) Validator(validate bool, cacheDir string) (validation.Schema, error) {
|
||||
func (f *ring1Factory) Validator(validate, openapi bool, cacheDir string) (validation.Schema, error) {
|
||||
if validate {
|
||||
if openapi {
|
||||
resources, err := f.OpenAPISchema(cacheDir)
|
||||
if err == nil {
|
||||
return validation.ConjunctiveSchema{
|
||||
openapivalidation.NewSchemaValidation(resources),
|
||||
validation.NoDoubleKeySchema{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
glog.Warningf("Failed to download OpenAPI (%v), falling back to swagger", err)
|
||||
}
|
||||
|
||||
discovery, err := f.clientAccessFactory.DiscoveryClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -395,16 +395,19 @@ func GetPodRunningTimeoutFlag(cmd *cobra.Command) (time.Duration, error) {
|
||||
func AddValidateFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("validate", true, "If true, use a schema to validate the input before sending it")
|
||||
cmd.Flags().String("schema-cache-dir", fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName))
|
||||
cmd.Flags().Bool("openapi-validation", false, "If true, use openapi rather than swagger for validation.")
|
||||
cmd.MarkFlagFilename("schema-cache-dir")
|
||||
}
|
||||
|
||||
func AddValidateOptionFlags(cmd *cobra.Command, options *ValidateOptions) {
|
||||
cmd.Flags().BoolVar(&options.EnableValidation, "validate", true, "If true, use a schema to validate the input before sending it")
|
||||
cmd.Flags().StringVar(&options.SchemaCacheDir, "schema-cache-dir", fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName))
|
||||
cmd.Flags().BoolVar(&options.UseOpenAPI, "openapi-validation", false, "If true, use openapi rather than swagger for validation")
|
||||
cmd.MarkFlagFilename("schema-cache-dir")
|
||||
}
|
||||
|
||||
func AddOpenAPIFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("openapi-validation", false, "If true, use openapi rather than swagger for validation")
|
||||
cmd.Flags().String("schema-cache-dir",
|
||||
fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName),
|
||||
fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'",
|
||||
@ -448,6 +451,7 @@ func AddGeneratorFlags(cmd *cobra.Command, defaultGenerator string) {
|
||||
|
||||
type ValidateOptions struct {
|
||||
EnableValidation bool
|
||||
UseOpenAPI bool
|
||||
SchemaCacheDir string
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ func NewSchemaValidation(resources openapi.Resources) *SchemaValidation {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *SchemaValidation) Validate(data []byte) error {
|
||||
func (v *SchemaValidation) ValidateBytes(data []byte) error {
|
||||
obj, err := parse(data)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -42,7 +42,7 @@ var _ = Describe("resource validation using OpenAPI Schema", func() {
|
||||
})
|
||||
|
||||
It("validates a valid pod", func() {
|
||||
err := validator.Validate([]byte(`
|
||||
err := validator.ValidateBytes([]byte(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
@ -64,7 +64,7 @@ spec:
|
||||
})
|
||||
|
||||
It("finds invalid command (string instead of []string) in Json Pod", func() {
|
||||
err := validator.Validate([]byte(`
|
||||
err := validator.ValidateBytes([]byte(`
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
@ -98,7 +98,7 @@ spec:
|
||||
})
|
||||
|
||||
It("fails because hostPort is string instead of int", func() {
|
||||
err := validator.Validate([]byte(`
|
||||
err := validator.ValidateBytes([]byte(`
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
@ -150,7 +150,7 @@ spec:
|
||||
})
|
||||
|
||||
It("fails because volume is not an array of object", func() {
|
||||
err := validator.Validate([]byte(`
|
||||
err := validator.ValidateBytes([]byte(`
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
@ -191,7 +191,7 @@ spec:
|
||||
})
|
||||
|
||||
It("fails because some string lists have empty strings", func() {
|
||||
err := validator.Validate([]byte(`
|
||||
err := validator.ValidateBytes([]byte(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
|
@ -56,7 +56,7 @@ func TestKubectlValidation(t *testing.T) {
|
||||
}
|
||||
cmdConfig := clientcmd.NewNonInteractiveClientConfig(*cfg, "test", &overrides, nil)
|
||||
factory := util.NewFactory(cmdConfig)
|
||||
schema, err := factory.Validator(true, "")
|
||||
schema, err := factory.Validator(true, true, "")
|
||||
if err != nil {
|
||||
t.Errorf("failed to get validator: %v", err)
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user