Fix validation by moving it into the resource builder.
Also always print an error for unknown field.
This commit is contained in:
parent
35c644a45f
commit
7f11585972
@ -104,9 +104,10 @@ func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, t
|
|||||||
for key, value := range fields {
|
for key, value := range fields {
|
||||||
details, ok := properties[key]
|
details, ok := properties[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
glog.Infof("unknown field: %s", key)
|
||||||
// Some properties can be missing because of
|
// Some properties can be missing because of
|
||||||
// https://github.com/GoogleCloudPlatform/kubernetes/issues/6842.
|
// https://github.com/GoogleCloudPlatform/kubernetes/issues/6842.
|
||||||
glog.V(2).Infof("couldn't find properties for %s", key)
|
glog.Info("this may be a false alarm, see https://github.com/GoogleCloudPlatform/kubernetes/issues/6842")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if details.Type == nil && details.Ref == nil {
|
if details.Type == nil && details.Ref == nil {
|
||||||
|
@ -79,6 +79,7 @@ func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) err
|
|||||||
|
|
||||||
mapper, typer := f.Object()
|
mapper, typer := f.Object()
|
||||||
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
|
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
|
||||||
|
Schema(schema).
|
||||||
ContinueOnError().
|
ContinueOnError().
|
||||||
NamespaceParam(cmdNamespace).RequireNamespace().
|
NamespaceParam(cmdNamespace).RequireNamespace().
|
||||||
FilenameParam(filenames...).
|
FilenameParam(filenames...).
|
||||||
@ -95,9 +96,6 @@ func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := schema.ValidateBytes(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, data)
|
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -143,7 +143,12 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
|
|||||||
var newRc *api.ReplicationController
|
var newRc *api.ReplicationController
|
||||||
|
|
||||||
if len(filename) != 0 {
|
if len(filename) != 0 {
|
||||||
|
schema, err := f.Validator()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
obj, err := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
|
obj, err := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
|
||||||
|
Schema(schema).
|
||||||
NamespaceParam(cmdNamespace).RequireNamespace().
|
NamespaceParam(cmdNamespace).RequireNamespace().
|
||||||
FilenameParam(filename).
|
FilenameParam(filename).
|
||||||
Do().
|
Do().
|
||||||
|
@ -93,6 +93,7 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
|
|||||||
|
|
||||||
mapper, typer := f.Object()
|
mapper, typer := f.Object()
|
||||||
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
|
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
|
||||||
|
Schema(schema).
|
||||||
ContinueOnError().
|
ContinueOnError().
|
||||||
NamespaceParam(cmdNamespace).RequireNamespace().
|
NamespaceParam(cmdNamespace).RequireNamespace().
|
||||||
FilenameParam(filenames...).
|
FilenameParam(filenames...).
|
||||||
@ -108,9 +109,6 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := schema.ValidateBytes(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
obj, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, true, data)
|
obj, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, true, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
@ -62,6 +63,8 @@ type Builder struct {
|
|||||||
|
|
||||||
singleResourceType bool
|
singleResourceType bool
|
||||||
continueOnError bool
|
continueOnError bool
|
||||||
|
|
||||||
|
schema validation.Schema
|
||||||
}
|
}
|
||||||
|
|
||||||
type resourceTuple struct {
|
type resourceTuple struct {
|
||||||
@ -77,6 +80,11 @@ func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Schema(schema validation.Schema) *Builder {
|
||||||
|
b.schema = schema
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// Filename is parameters passed via a filename argument which may be URLs, the "-" argument indicating
|
// Filename is parameters passed via a filename argument which may be URLs, the "-" argument indicating
|
||||||
// STDIN, or paths to files or directories. If ContinueOnError() is set prior to this method being called,
|
// STDIN, or paths to files or directories. If ContinueOnError() is set prior to this method being called,
|
||||||
// objects on the path that are unrecognized will be ignored (but logged at V(2)).
|
// objects on the path that are unrecognized will be ignored (but logged at V(2)).
|
||||||
@ -105,6 +113,7 @@ func (b *Builder) URL(urls ...*url.URL) *Builder {
|
|||||||
b.paths = append(b.paths, &URLVisitor{
|
b.paths = append(b.paths, &URLVisitor{
|
||||||
Mapper: b.mapper,
|
Mapper: b.mapper,
|
||||||
URL: u,
|
URL: u,
|
||||||
|
Schema: b.schema,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
@ -123,7 +132,7 @@ func (b *Builder) Stdin() *Builder {
|
|||||||
// will be ignored (but logged at V(2)).
|
// will be ignored (but logged at V(2)).
|
||||||
func (b *Builder) Stream(r io.Reader, name string) *Builder {
|
func (b *Builder) Stream(r io.Reader, name string) *Builder {
|
||||||
b.stream = true
|
b.stream = true
|
||||||
b.paths = append(b.paths, NewStreamVisitor(r, b.mapper, name, b.continueOnError))
|
b.paths = append(b.paths, NewStreamVisitor(r, b.mapper, b.schema, name, b.continueOnError))
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,12 +159,14 @@ func (b *Builder) Path(paths ...string) *Builder {
|
|||||||
Extensions: []string{".json", ".yaml", ".yml"},
|
Extensions: []string{".json", ".yaml", ".yml"},
|
||||||
Recursive: false,
|
Recursive: false,
|
||||||
IgnoreErrors: b.continueOnError,
|
IgnoreErrors: b.continueOnError,
|
||||||
|
Schema: b.schema,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
visitor = &PathVisitor{
|
visitor = &PathVisitor{
|
||||||
Mapper: b.mapper,
|
Mapper: b.mapper,
|
||||||
Path: p,
|
Path: p,
|
||||||
IgnoreErrors: b.continueOnError,
|
IgnoreErrors: b.continueOnError,
|
||||||
|
Schema: b.schema,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.paths = append(b.paths, visitor)
|
b.paths = append(b.paths, visitor)
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/yaml"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/yaml"
|
||||||
@ -180,6 +181,20 @@ func (l EagerVisitorList) Visit(fn VisitorFunc) error {
|
|||||||
return errors.NewAggregate(errs)
|
return errors.NewAggregate(errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidateSchema(data []byte, schema validation.Schema) error {
|
||||||
|
if schema == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data, err := yaml.ToJSON(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error converting to YAML: %v", err)
|
||||||
|
}
|
||||||
|
if err := schema.ValidateBytes(data); err != nil {
|
||||||
|
return fmt.Errorf("error validating data: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// PathVisitor visits a given path and returns an object representing the file
|
// PathVisitor visits a given path and returns an object representing the file
|
||||||
// at that path.
|
// at that path.
|
||||||
type PathVisitor struct {
|
type PathVisitor struct {
|
||||||
@ -188,6 +203,8 @@ type PathVisitor struct {
|
|||||||
Path string
|
Path string
|
||||||
// Whether to ignore files that are not recognized as API objects
|
// Whether to ignore files that are not recognized as API objects
|
||||||
IgnoreErrors bool
|
IgnoreErrors bool
|
||||||
|
// Schema for validation
|
||||||
|
Schema validation.Schema
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PathVisitor) Visit(fn VisitorFunc) error {
|
func (v *PathVisitor) Visit(fn VisitorFunc) error {
|
||||||
@ -195,6 +212,9 @@ func (v *PathVisitor) Visit(fn VisitorFunc) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to read %q: %v", v.Path, err)
|
return fmt.Errorf("unable to read %q: %v", v.Path, err)
|
||||||
}
|
}
|
||||||
|
if err := ValidateSchema(data, v.Schema); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
info, err := v.Mapper.InfoForData(data, v.Path)
|
info, err := v.Mapper.InfoForData(data, v.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if v.IgnoreErrors {
|
if v.IgnoreErrors {
|
||||||
@ -218,6 +238,8 @@ type DirectoryVisitor struct {
|
|||||||
Extensions []string
|
Extensions []string
|
||||||
// Whether to ignore files that are not recognized as API objects
|
// Whether to ignore files that are not recognized as API objects
|
||||||
IgnoreErrors bool
|
IgnoreErrors bool
|
||||||
|
// Schema for validation
|
||||||
|
Schema validation.Schema
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *DirectoryVisitor) ignoreFile(path string) bool {
|
func (v *DirectoryVisitor) ignoreFile(path string) bool {
|
||||||
@ -253,6 +275,9 @@ func (v *DirectoryVisitor) Visit(fn VisitorFunc) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to read %q: %v", path, err)
|
return fmt.Errorf("unable to read %q: %v", path, err)
|
||||||
}
|
}
|
||||||
|
if err := ValidateSchema(data, v.Schema); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
info, err := v.Mapper.InfoForData(data, path)
|
info, err := v.Mapper.InfoForData(data, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if v.IgnoreErrors {
|
if v.IgnoreErrors {
|
||||||
@ -269,7 +294,8 @@ func (v *DirectoryVisitor) Visit(fn VisitorFunc) error {
|
|||||||
// an info object representing the downloaded object.
|
// an info object representing the downloaded object.
|
||||||
type URLVisitor struct {
|
type URLVisitor struct {
|
||||||
*Mapper
|
*Mapper
|
||||||
URL *url.URL
|
URL *url.URL
|
||||||
|
Schema validation.Schema
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *URLVisitor) Visit(fn VisitorFunc) error {
|
func (v *URLVisitor) Visit(fn VisitorFunc) error {
|
||||||
@ -285,6 +311,9 @@ func (v *URLVisitor) Visit(fn VisitorFunc) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to read URL %q: %v\n", v.URL, err)
|
return fmt.Errorf("unable to read URL %q: %v\n", v.URL, err)
|
||||||
}
|
}
|
||||||
|
if err := ValidateSchema(data, v.Schema); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
info, err := v.Mapper.InfoForData(data, v.URL.String())
|
info, err := v.Mapper.InfoForData(data, v.URL.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -379,6 +408,7 @@ type StreamVisitor struct {
|
|||||||
|
|
||||||
Source string
|
Source string
|
||||||
IgnoreErrors bool
|
IgnoreErrors bool
|
||||||
|
Schema validation.Schema
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStreamVisitor creates a visitor that will return resources that were encoded into the provided
|
// NewStreamVisitor creates a visitor that will return resources that were encoded into the provided
|
||||||
@ -386,8 +416,8 @@ type StreamVisitor struct {
|
|||||||
// empty stream is treated as an error for now.
|
// empty stream is treated as an error for now.
|
||||||
// TODO: convert ignoreErrors into a func(data, error, count) bool that consumers can use to decide
|
// TODO: convert ignoreErrors into a func(data, error, count) bool that consumers can use to decide
|
||||||
// what to do with ignored errors.
|
// what to do with ignored errors.
|
||||||
func NewStreamVisitor(r io.Reader, mapper *Mapper, source string, ignoreErrors bool) Visitor {
|
func NewStreamVisitor(r io.Reader, mapper *Mapper, schema validation.Schema, source string, ignoreErrors bool) Visitor {
|
||||||
return &StreamVisitor{r, mapper, source, ignoreErrors}
|
return &StreamVisitor{r, mapper, source, ignoreErrors, schema}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visit implements Visitor over a stream.
|
// Visit implements Visitor over a stream.
|
||||||
@ -405,6 +435,9 @@ func (v *StreamVisitor) Visit(fn VisitorFunc) error {
|
|||||||
if len(ext.RawJSON) == 0 || bytes.Equal(ext.RawJSON, []byte("null")) {
|
if len(ext.RawJSON) == 0 || bytes.Equal(ext.RawJSON, []byte("null")) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if err := ValidateSchema(ext.RawJSON, v.Schema); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
info, err := v.InfoForData(ext.RawJSON, v.Source)
|
info, err := v.InfoForData(ext.RawJSON, v.Source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if v.IgnoreErrors {
|
if v.IgnoreErrors {
|
||||||
|
Loading…
Reference in New Issue
Block a user