mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Add some checking for the presence of the same key twice.
This commit is contained in:
		@@ -17,6 +17,7 @@ limitations under the License.
 | 
			
		||||
package validation
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
@@ -24,6 +25,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/emicklei/go-restful/swagger"
 | 
			
		||||
	ejson "github.com/exponent-io/jsonpath"
 | 
			
		||||
	"github.com/golang/glog"
 | 
			
		||||
	apiutil "k8s.io/kubernetes/pkg/api/util"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
			
		||||
@@ -62,6 +64,69 @@ type NullSchema struct{}
 | 
			
		||||
 | 
			
		||||
func (NullSchema) ValidateBytes(data []byte) error { return nil }
 | 
			
		||||
 | 
			
		||||
type NoDoubleKeySchema struct{}
 | 
			
		||||
 | 
			
		||||
func (NoDoubleKeySchema) ValidateBytes(data []byte) error {
 | 
			
		||||
	var list []error = nil
 | 
			
		||||
	if err := validateNoDuplicateKeys(data, "metadata", "labels"); err != nil {
 | 
			
		||||
		list = append(list, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := validateNoDuplicateKeys(data, "metadata", "annotations"); err != nil {
 | 
			
		||||
		list = append(list, err)
 | 
			
		||||
	}
 | 
			
		||||
	return utilerrors.NewAggregate(list)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateNoDuplicateKeys(data []byte, path ...string) error {
 | 
			
		||||
	r := ejson.NewDecoder(bytes.NewReader(data))
 | 
			
		||||
	// This is Go being unfriendly. The 'path ...string' comes in as a
 | 
			
		||||
	// []string, and SeekTo takes ...interface{}, so we can't just pass
 | 
			
		||||
	// the path straight in, we have to copy it.  *sigh*
 | 
			
		||||
	ifacePath := []interface{}{}
 | 
			
		||||
	for ix := range path {
 | 
			
		||||
		ifacePath = append(ifacePath, path[ix])
 | 
			
		||||
	}
 | 
			
		||||
	found, err := r.SeekTo(ifacePath...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !found {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	seen := map[string]bool{}
 | 
			
		||||
	for {
 | 
			
		||||
		tok, err := r.Token()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		switch t := tok.(type) {
 | 
			
		||||
		case json.Delim:
 | 
			
		||||
			if t.String() == "}" {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		case ejson.KeyString:
 | 
			
		||||
			if seen[string(t)] {
 | 
			
		||||
				return fmt.Errorf("duplicate key: %s", string(t))
 | 
			
		||||
			} else {
 | 
			
		||||
				seen[string(t)] = true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ConjunctiveSchema []Schema
 | 
			
		||||
 | 
			
		||||
func (c ConjunctiveSchema) ValidateBytes(data []byte) error {
 | 
			
		||||
	var list []error = nil
 | 
			
		||||
	schemas := []Schema(c)
 | 
			
		||||
	for ix := range schemas {
 | 
			
		||||
		if err := schemas[ix].ValidateBytes(data); err != nil {
 | 
			
		||||
			list = append(list, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return utilerrors.NewAggregate(list)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SwaggerSchema struct {
 | 
			
		||||
	api      swagger.ApiDeclaration
 | 
			
		||||
	delegate Schema // For delegating to other api groups
 | 
			
		||||
 
 | 
			
		||||
@@ -309,3 +309,118 @@ func TestTypeAny(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidateDuplicateLabelsFailCases(t *testing.T) {
 | 
			
		||||
	strs := []string{
 | 
			
		||||
		`{
 | 
			
		||||
	"metadata": {
 | 
			
		||||
		"labels": {
 | 
			
		||||
			"foo": "bar",
 | 
			
		||||
			"foo": "baz"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}`,
 | 
			
		||||
		`{
 | 
			
		||||
	"metadata": {
 | 
			
		||||
		"annotations": {
 | 
			
		||||
			"foo": "bar",
 | 
			
		||||
			"foo": "baz"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}`,
 | 
			
		||||
		`{
 | 
			
		||||
	"metadata": {
 | 
			
		||||
		"labels": {
 | 
			
		||||
			"foo": "blah"
 | 
			
		||||
		},
 | 
			
		||||
		"annotations": {
 | 
			
		||||
			"foo": "bar",
 | 
			
		||||
			"foo": "baz"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}`,
 | 
			
		||||
	}
 | 
			
		||||
	schema := NoDoubleKeySchema{}
 | 
			
		||||
	for _, str := range strs {
 | 
			
		||||
		err := schema.ValidateBytes([]byte(str))
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			t.Errorf("Unexpected non-error %s", str)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidateDuplicateLabelsPassCases(t *testing.T) {
 | 
			
		||||
	strs := []string{
 | 
			
		||||
		`{
 | 
			
		||||
	"metadata": {
 | 
			
		||||
		"labels": {
 | 
			
		||||
			"foo": "bar"
 | 
			
		||||
		},
 | 
			
		||||
		"annotations": {
 | 
			
		||||
			"foo": "baz"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}`,
 | 
			
		||||
		`{
 | 
			
		||||
	"metadata": {}
 | 
			
		||||
}`,
 | 
			
		||||
		`{
 | 
			
		||||
	"metadata": {
 | 
			
		||||
		"labels": {}
 | 
			
		||||
	}
 | 
			
		||||
}`,
 | 
			
		||||
	}
 | 
			
		||||
	schema := NoDoubleKeySchema{}
 | 
			
		||||
	for _, str := range strs {
 | 
			
		||||
		err := schema.ValidateBytes([]byte(str))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("Unexpected error: %v %s", err, str)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AlwaysInvalidSchema struct{}
 | 
			
		||||
 | 
			
		||||
func (AlwaysInvalidSchema) ValidateBytes([]byte) error {
 | 
			
		||||
	return fmt.Errorf("Always invalid!")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestConjunctiveSchema(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		schemas    []Schema
 | 
			
		||||
		shouldPass bool
 | 
			
		||||
		name       string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			schemas:    []Schema{NullSchema{}, NullSchema{}},
 | 
			
		||||
			shouldPass: true,
 | 
			
		||||
			name:       "all pass",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			schemas:    []Schema{NullSchema{}, AlwaysInvalidSchema{}},
 | 
			
		||||
			shouldPass: false,
 | 
			
		||||
			name:       "one fail",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			schemas:    []Schema{AlwaysInvalidSchema{}, AlwaysInvalidSchema{}},
 | 
			
		||||
			shouldPass: false,
 | 
			
		||||
			name:       "all fail",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			schemas:    []Schema{},
 | 
			
		||||
			shouldPass: true,
 | 
			
		||||
			name:       "empty",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		schema := ConjunctiveSchema(test.schemas)
 | 
			
		||||
		err := schema.ValidateBytes([]byte{})
 | 
			
		||||
		if err != nil && test.shouldPass {
 | 
			
		||||
			t.Errorf("Unexpected error: %v in %s", err, test.name)
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil && !test.shouldPass {
 | 
			
		||||
			t.Errorf("Unexpected non-error: %s", test.name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -762,10 +762,14 @@ func (f *factory) Validator(validate bool, cacheDir string) (validation.Schema,
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return &clientSwaggerSchema{
 | 
			
		||||
		swaggerSchema := &clientSwaggerSchema{
 | 
			
		||||
			c:        restclient,
 | 
			
		||||
			fedc:     fedClient,
 | 
			
		||||
			cacheDir: dir,
 | 
			
		||||
		}
 | 
			
		||||
		return validation.ConjunctiveSchema{
 | 
			
		||||
			swaggerSchema,
 | 
			
		||||
			validation.NoDoubleKeySchema{},
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
	return validation.NullSchema{}, nil
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user