mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Do not automatically decode runtime.RawExtension
Make clients opt in to decoding objects that are stored in the generic api.List object by invoking runtime.DecodeList() with a set of schemes. Makes it easier to handle unknown schema objects because decoding is in the control of the code. Add runtime.Unstructured, which is a simple in memory representation of an external object.
This commit is contained in:
		@@ -115,12 +115,14 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		func(j *api.List, c fuzz.Continue) {
 | 
							func(j *api.List, c fuzz.Continue) {
 | 
				
			||||||
			c.FuzzNoCustom(j) // fuzz self without calling this function again
 | 
								c.FuzzNoCustom(j) // fuzz self without calling this function again
 | 
				
			||||||
			if j.Items == nil {
 | 
								// TODO: uncomment when round trip starts from a versioned object
 | 
				
			||||||
 | 
								if false { //j.Items == nil {
 | 
				
			||||||
				j.Items = []runtime.Object{}
 | 
									j.Items = []runtime.Object{}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		func(j *runtime.Object, c fuzz.Continue) {
 | 
							func(j *runtime.Object, c fuzz.Continue) {
 | 
				
			||||||
			if c.RandBool() {
 | 
								// TODO: uncomment when round trip starts from a versioned object
 | 
				
			||||||
 | 
								if true { //c.RandBool() {
 | 
				
			||||||
				*j = &runtime.Unknown{
 | 
									*j = &runtime.Unknown{
 | 
				
			||||||
					TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"},
 | 
										TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"},
 | 
				
			||||||
					RawJSON:  []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`),
 | 
										RawJSON:  []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// AddObjectsFromPath loads the JSON or YAML file containing Kubernetes API resources
 | 
					// AddObjectsFromPath loads the JSON or YAML file containing Kubernetes API resources
 | 
				
			||||||
// and adds them to the provided ObjectRetriever.
 | 
					// and adds them to the provided ObjectRetriever.
 | 
				
			||||||
func AddObjectsFromPath(path string, o ObjectRetriever) error {
 | 
					func AddObjectsFromPath(path string, o ObjectRetriever, decoder runtime.Decoder) error {
 | 
				
			||||||
	data, err := ioutil.ReadFile(path)
 | 
						data, err := ioutil.ReadFile(path)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -90,7 +90,7 @@ func AddObjectsFromPath(path string, o ObjectRetriever) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	obj, err := api.Codec.Decode(data)
 | 
						obj, err := decoder.Decode(data)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -103,17 +103,12 @@ func AddObjectsFromPath(path string, o ObjectRetriever) error {
 | 
				
			|||||||
type objects struct {
 | 
					type objects struct {
 | 
				
			||||||
	types   map[string][]runtime.Object
 | 
						types   map[string][]runtime.Object
 | 
				
			||||||
	last    map[string]int
 | 
						last    map[string]int
 | 
				
			||||||
	typer   runtime.ObjectTyper
 | 
						scheme  runtime.ObjectScheme
 | 
				
			||||||
	creater runtime.ObjectCreater
 | 
						decoder runtime.ObjectDecoder
 | 
				
			||||||
	copier  copier
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ ObjectRetriever = &objects{}
 | 
					var _ ObjectRetriever = &objects{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type copier interface {
 | 
					 | 
				
			||||||
	Copy(obj runtime.Object) (runtime.Object, error)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewObjects implements the ObjectRetriever interface by introspecting the
 | 
					// NewObjects implements the ObjectRetriever interface by introspecting the
 | 
				
			||||||
// objects provided to Add() and returning them when the Kind method is invoked.
 | 
					// objects provided to Add() and returning them when the Kind method is invoked.
 | 
				
			||||||
// If an api.List object is provided to Add(), each child item is added. If an
 | 
					// If an api.List object is provided to Add(), each child item is added. If an
 | 
				
			||||||
@@ -124,18 +119,17 @@ type copier interface {
 | 
				
			|||||||
// as a runtime.Object if Status == Success).  If multiple PodLists are provided, they
 | 
					// as a runtime.Object if Status == Success).  If multiple PodLists are provided, they
 | 
				
			||||||
// will be returned in order by the Kind call, and the last PodList will be reused for
 | 
					// will be returned in order by the Kind call, and the last PodList will be reused for
 | 
				
			||||||
// subsequent calls.
 | 
					// subsequent calls.
 | 
				
			||||||
func NewObjects(scheme *runtime.Scheme) ObjectRetriever {
 | 
					func NewObjects(scheme runtime.ObjectScheme, decoder runtime.ObjectDecoder) ObjectRetriever {
 | 
				
			||||||
	return objects{
 | 
						return objects{
 | 
				
			||||||
		types:   make(map[string][]runtime.Object),
 | 
							types:   make(map[string][]runtime.Object),
 | 
				
			||||||
		last:    make(map[string]int),
 | 
							last:    make(map[string]int),
 | 
				
			||||||
		typer:   scheme,
 | 
							scheme:  scheme,
 | 
				
			||||||
		creater: scheme,
 | 
							decoder: decoder,
 | 
				
			||||||
		copier:  scheme,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o objects) Kind(kind, name string) (runtime.Object, error) {
 | 
					func (o objects) Kind(kind, name string) (runtime.Object, error) {
 | 
				
			||||||
	empty, _ := o.creater.New("", kind)
 | 
						empty, _ := o.scheme.New("", kind)
 | 
				
			||||||
	nilValue := reflect.Zero(reflect.TypeOf(empty)).Interface().(runtime.Object)
 | 
						nilValue := reflect.Zero(reflect.TypeOf(empty)).Interface().(runtime.Object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	arr, ok := o.types[kind]
 | 
						arr, ok := o.types[kind]
 | 
				
			||||||
@@ -146,14 +140,14 @@ func (o objects) Kind(kind, name string) (runtime.Object, error) {
 | 
				
			|||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				return empty, nil
 | 
									return empty, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			out, err := o.creater.New("", kind)
 | 
								out, err := o.scheme.New("", kind)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nilValue, err
 | 
									return nilValue, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if err := runtime.SetList(out, arr); err != nil {
 | 
								if err := runtime.SetList(out, arr); err != nil {
 | 
				
			||||||
				return nilValue, err
 | 
									return nilValue, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if out, err = o.copier.Copy(out); err != nil {
 | 
								if out, err = o.scheme.Copy(out); err != nil {
 | 
				
			||||||
				return nilValue, err
 | 
									return nilValue, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return out, nil
 | 
								return out, nil
 | 
				
			||||||
@@ -168,7 +162,7 @@ func (o objects) Kind(kind, name string) (runtime.Object, error) {
 | 
				
			|||||||
	if index < 0 {
 | 
						if index < 0 {
 | 
				
			||||||
		return nilValue, errors.NewNotFound(kind, name)
 | 
							return nilValue, errors.NewNotFound(kind, name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	out, err := o.copier.Copy(arr[index])
 | 
						out, err := o.scheme.Copy(arr[index])
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nilValue, err
 | 
							return nilValue, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -187,7 +181,7 @@ func (o objects) Kind(kind, name string) (runtime.Object, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o objects) Add(obj runtime.Object) error {
 | 
					func (o objects) Add(obj runtime.Object) error {
 | 
				
			||||||
	_, kind, err := o.typer.ObjectVersionAndKind(obj)
 | 
						_, kind, err := o.scheme.ObjectVersionAndKind(obj)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -202,6 +196,9 @@ func (o objects) Add(obj runtime.Object) error {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if errs := runtime.DecodeList(list, o.decoder); len(errs) > 0 {
 | 
				
			||||||
 | 
								return errs[0]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		for _, obj := range list {
 | 
							for _, obj := range list {
 | 
				
			||||||
			if err := o.Add(obj); err != nil {
 | 
								if err := o.Add(obj); err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NewSimpleFake returns a client that will respond with the provided objects
 | 
					// NewSimpleFake returns a client that will respond with the provided objects
 | 
				
			||||||
func NewSimpleFake(objects ...runtime.Object) *Fake {
 | 
					func NewSimpleFake(objects ...runtime.Object) *Fake {
 | 
				
			||||||
	o := NewObjects(api.Scheme)
 | 
						o := NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
	for _, obj := range objects {
 | 
						for _, obj := range objects {
 | 
				
			||||||
		if err := o.Add(obj); err != nil {
 | 
							if err := o.Add(obj); err != nil {
 | 
				
			||||||
			panic(err)
 | 
								panic(err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,8 +27,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestNewClient(t *testing.T) {
 | 
					func TestNewClient(t *testing.T) {
 | 
				
			||||||
	o := NewObjects(api.Scheme)
 | 
						o := NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
	if err := AddObjectsFromPath("../../../examples/guestbook/frontend-service.json", o); err != nil {
 | 
						if err := AddObjectsFromPath("../../../examples/guestbook/frontend-service.json", o, api.Scheme); err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	client := &Fake{ReactFn: ObjectReaction(o, latest.RESTMapper)}
 | 
						client := &Fake{ReactFn: ObjectReaction(o, latest.RESTMapper)}
 | 
				
			||||||
@@ -52,7 +52,7 @@ func TestNewClient(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestErrors(t *testing.T) {
 | 
					func TestErrors(t *testing.T) {
 | 
				
			||||||
	o := NewObjects(api.Scheme)
 | 
						o := NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
	o.Add(&api.List{
 | 
						o.Add(&api.List{
 | 
				
			||||||
		Items: []runtime.Object{
 | 
							Items: []runtime.Object{
 | 
				
			||||||
			// This first call to List will return this error
 | 
								// This first call to List will return this error
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,10 @@ type missingKindErr struct {
 | 
				
			|||||||
	data string
 | 
						data string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewMissingKindErr(data string) error {
 | 
				
			||||||
 | 
						return &missingKindErr{data}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (k *missingKindErr) Error() string {
 | 
					func (k *missingKindErr) Error() string {
 | 
				
			||||||
	return fmt.Sprintf("Object 'Kind' is missing in '%s'", k.data)
 | 
						return fmt.Sprintf("Object 'Kind' is missing in '%s'", k.data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -70,6 +74,10 @@ type missingVersionErr struct {
 | 
				
			|||||||
	data string
 | 
						data string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewMissingVersionErr(data string) error {
 | 
				
			||||||
 | 
						return &missingVersionErr{data}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (k *missingVersionErr) Error() string {
 | 
					func (k *missingVersionErr) Error() string {
 | 
				
			||||||
	return fmt.Sprintf("Object 'apiVersion' is missing in '%s'", k.data)
 | 
						return fmt.Sprintf("Object 'apiVersion' is missing in '%s'", k.data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -238,6 +238,17 @@ func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Recognizes returns true if the scheme is able to handle the provided version and kind
 | 
				
			||||||
 | 
					// of an object.
 | 
				
			||||||
 | 
					func (s *Scheme) Recognizes(version, kind string) bool {
 | 
				
			||||||
 | 
						m, ok := s.versionMap[version]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, ok = m[kind]
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RegisterInputDefaults sets the provided field mapping function and field matching
 | 
					// RegisterInputDefaults sets the provided field mapping function and field matching
 | 
				
			||||||
// as the defaults for the provided input type.  The fn may be nil, in which case no
 | 
					// as the defaults for the provided input type.  The fn may be nil, in which case no
 | 
				
			||||||
// mapping will happen by default. Use this method to register a mechanism for handling
 | 
					// mapping will happen by default. Use this method to register a mechanism for handling
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -393,6 +393,17 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("unexpected error: %v", err)
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						list, err := runtime.ExtractList(out)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if errs := runtime.DecodeList(list, api.Scheme); len(errs) > 0 {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", errs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := runtime.SetList(out, list); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	expected := &api.List{
 | 
						expected := &api.List{
 | 
				
			||||||
		Items: []runtime.Object{
 | 
							Items: []runtime.Object{
 | 
				
			||||||
			&pods.Items[0],
 | 
								&pods.Items[0],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -347,6 +347,12 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return fn(info)
 | 
								return fn(info)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if errs := runtime.DecodeList(items, struct {
 | 
				
			||||||
 | 
								runtime.ObjectTyper
 | 
				
			||||||
 | 
								runtime.Decoder
 | 
				
			||||||
 | 
							}{v.Mapper, info.Mapping.Codec}); len(errs) > 0 {
 | 
				
			||||||
 | 
								return errors.NewAggregate(errs)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		for i := range items {
 | 
							for i := range items {
 | 
				
			||||||
			item, err := v.InfoForObject(items[i])
 | 
								item, err := v.InfoForObject(items[i])
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,7 +135,7 @@ func TestSyncNamespaceThatIsActive(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRunStop(t *testing.T) {
 | 
					func TestRunStop(t *testing.T) {
 | 
				
			||||||
	o := testclient.NewObjects(api.Scheme)
 | 
						o := testclient.NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
	client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
 | 
						client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
 | 
				
			||||||
	nsMgr := NewNamespaceManager(client, 1*time.Second)
 | 
						nsMgr := NewNamespaceManager(client, 1*time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,8 +21,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CodecFor returns a Codec that invokes Encode with the provided version.
 | 
					// CodecFor returns a Codec that invokes Encode with the provided version.
 | 
				
			||||||
func CodecFor(scheme *Scheme, version string) Codec {
 | 
					func CodecFor(codec ObjectCodec, version string) Codec {
 | 
				
			||||||
	return &codecWrapper{scheme, version}
 | 
						return &codecWrapper{codec, version}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// yamlCodec converts YAML passed to the Decoder methods to JSON.
 | 
					// yamlCodec converts YAML passed to the Decoder methods to JSON.
 | 
				
			||||||
@@ -69,11 +69,11 @@ func EncodeOrDie(codec Codec, obj Object) string {
 | 
				
			|||||||
// codecWrapper implements encoding to an alternative
 | 
					// codecWrapper implements encoding to an alternative
 | 
				
			||||||
// default version for a scheme.
 | 
					// default version for a scheme.
 | 
				
			||||||
type codecWrapper struct {
 | 
					type codecWrapper struct {
 | 
				
			||||||
	*Scheme
 | 
						ObjectCodec
 | 
				
			||||||
	version string
 | 
						version string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Encode implements Codec
 | 
					// Encode implements Codec
 | 
				
			||||||
func (c *codecWrapper) Encode(obj Object) ([]byte, error) {
 | 
					func (c *codecWrapper) Encode(obj Object) ([]byte, error) {
 | 
				
			||||||
	return c.Scheme.EncodeToVersion(obj, c.version)
 | 
						return c.EncodeToVersion(obj, c.version)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,9 +63,22 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
 | 
				
			|||||||
	s.AddKnownTypes("", &ObjectTest{})
 | 
						s.AddKnownTypes("", &ObjectTest{})
 | 
				
			||||||
	s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
 | 
						s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{}]}`))
 | 
						obj, err := s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{}]}`))
 | 
				
			||||||
	if err == nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("unexpected non-error")
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := obj.(*ObjectTest)
 | 
				
			||||||
 | 
						if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != "{}" {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected object: %#v", test.Items[0])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						obj, err = s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{"kind":"Other","apiVersion":"v1"}]}`))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test = obj.(*ObjectTest)
 | 
				
			||||||
 | 
						if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "Other" || unk.APIVersion != "v1" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected object: %#v", test.Items[0])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -99,17 +112,34 @@ func TestArrayOfRuntimeObject(t *testing.T) {
 | 
				
			|||||||
	if err := json.Unmarshal(wire, obj); err != nil {
 | 
						if err := json.Unmarshal(wire, obj); err != nil {
 | 
				
			||||||
		t.Fatalf("unexpected error: %v", err)
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	t.Logf("exact wire is: %#v", string(obj.Items[0].RawJSON))
 | 
						t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	decoded, err := s.Decode(wire)
 | 
						decoded, err := s.Decode(wire)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("unexpected error: %v", err)
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						list, err := runtime.ExtractList(decoded)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if errs := runtime.DecodeList(list, s); len(errs) > 0 {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", errs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list2, err := runtime.ExtractList(list[3])
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if errs := runtime.DecodeList(list2, s); len(errs) > 0 {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", errs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := runtime.SetList(list[3], list2); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	internal.Items[2].(*runtime.Unknown).Kind = "OtherTest"
 | 
						internal.Items[2].(*runtime.Unknown).Kind = "OtherTest"
 | 
				
			||||||
	internal.Items[2].(*runtime.Unknown).APIVersion = "unknown"
 | 
						internal.Items[2].(*runtime.Unknown).APIVersion = "unknown"
 | 
				
			||||||
	if e, a := internal, decoded; !reflect.DeepEqual(e, a) {
 | 
						if e, a := internal.Items, list; !reflect.DeepEqual(e, a) {
 | 
				
			||||||
		t.Log(string(decoded.(*ObjectTest).Items[2].(*runtime.Unknown).RawJSON))
 | 
					 | 
				
			||||||
		t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a))
 | 
							t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,7 @@ import (
 | 
				
			|||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: move me to pkg/api/meta
 | 
					// IsListType returns true if the provided Object has a slice called Items
 | 
				
			||||||
func IsListType(obj Object) bool {
 | 
					func IsListType(obj Object) bool {
 | 
				
			||||||
	_, err := GetItemsPtr(obj)
 | 
						_, err := GetItemsPtr(obj)
 | 
				
			||||||
	return err == nil
 | 
						return err == nil
 | 
				
			||||||
@@ -33,7 +33,6 @@ func IsListType(obj Object) bool {
 | 
				
			|||||||
// If 'list' doesn't have an Items member, it's not really a list type
 | 
					// If 'list' doesn't have an Items member, it's not really a list type
 | 
				
			||||||
// and an error will be returned.
 | 
					// and an error will be returned.
 | 
				
			||||||
// This function will either return a pointer to a slice, or an error, but not both.
 | 
					// This function will either return a pointer to a slice, or an error, but not both.
 | 
				
			||||||
// TODO: move me to pkg/api/meta
 | 
					 | 
				
			||||||
func GetItemsPtr(list Object) (interface{}, error) {
 | 
					func GetItemsPtr(list Object) (interface{}, error) {
 | 
				
			||||||
	v, err := conversion.EnforcePtr(list)
 | 
						v, err := conversion.EnforcePtr(list)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -150,9 +149,36 @@ func FieldPtr(v reflect.Value, fieldName string, dest interface{}) error {
 | 
				
			|||||||
	return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), v.Type())
 | 
						return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), v.Type())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DecodeList alters the list in place, attempting to decode any objects found in
 | 
				
			||||||
 | 
					// the list that have the runtime.Unknown type. Any errors that occur are returned
 | 
				
			||||||
 | 
					// after the entire list is processed. Decoders are tried in order.
 | 
				
			||||||
 | 
					func DecodeList(objects []Object, decoders ...ObjectDecoder) []error {
 | 
				
			||||||
 | 
						errs := []error(nil)
 | 
				
			||||||
 | 
						for i, obj := range objects {
 | 
				
			||||||
 | 
							switch t := obj.(type) {
 | 
				
			||||||
 | 
							case *Unknown:
 | 
				
			||||||
 | 
								for _, decoder := range decoders {
 | 
				
			||||||
 | 
									if !decoder.Recognizes(t.APIVersion, t.Kind) {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									obj, err := decoder.Decode(t.RawJSON)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										errs = append(errs, err)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									objects[i] = obj
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return errs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MultiObjectTyper returns the types of objects across multiple schemes in order.
 | 
					// MultiObjectTyper returns the types of objects across multiple schemes in order.
 | 
				
			||||||
type MultiObjectTyper []ObjectTyper
 | 
					type MultiObjectTyper []ObjectTyper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ ObjectTyper = MultiObjectTyper{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m MultiObjectTyper) DataVersionAndKind(data []byte) (version, kind string, err error) {
 | 
					func (m MultiObjectTyper) DataVersionAndKind(data []byte) (version, kind string, err error) {
 | 
				
			||||||
	for _, t := range m {
 | 
						for _, t := range m {
 | 
				
			||||||
		version, kind, err = t.DataVersionAndKind(data)
 | 
							version, kind, err = t.DataVersionAndKind(data)
 | 
				
			||||||
@@ -162,6 +188,7 @@ func (m MultiObjectTyper) DataVersionAndKind(data []byte) (version, kind string,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m MultiObjectTyper) ObjectVersionAndKind(obj Object) (version, kind string, err error) {
 | 
					func (m MultiObjectTyper) ObjectVersionAndKind(obj Object) (version, kind string, err error) {
 | 
				
			||||||
	for _, t := range m {
 | 
						for _, t := range m {
 | 
				
			||||||
		version, kind, err = t.ObjectVersionAndKind(obj)
 | 
							version, kind, err = t.ObjectVersionAndKind(obj)
 | 
				
			||||||
@@ -171,3 +198,12 @@ func (m MultiObjectTyper) ObjectVersionAndKind(obj Object) (version, kind string
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m MultiObjectTyper) Recognizes(version, kind string) bool {
 | 
				
			||||||
 | 
						for _, t := range m {
 | 
				
			||||||
 | 
							if t.Recognizes(version, kind) {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ import (
 | 
				
			|||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -132,6 +133,22 @@ func TestExtractListOfValuePtrs(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDecodeList(t *testing.T) {
 | 
				
			||||||
 | 
						pl := &api.List{
 | 
				
			||||||
 | 
							Items: []runtime.Object{
 | 
				
			||||||
 | 
								&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
								&runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: "v1beta3"}, RawJSON: []byte(`{"kind":"Pod","apiVersion":"v1beta3","metadata":{"name":"test"}}`)},
 | 
				
			||||||
 | 
								&runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if errs := runtime.DecodeList(pl.Items, api.Scheme); len(errs) != 0 {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error %v", errs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if pod, ok := pl.Items[1].(*api.Pod); !ok || pod.Name != "test" {
 | 
				
			||||||
 | 
							t.Errorf("object not converted: %#v", pl.Items[1])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestSetList(t *testing.T) {
 | 
					func TestSetList(t *testing.T) {
 | 
				
			||||||
	pl := &api.PodList{}
 | 
						pl := &api.PodList{}
 | 
				
			||||||
	list := []runtime.Object{
 | 
						list := []runtime.Object{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,24 @@ limitations under the License.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package runtime
 | 
					package runtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ObjectScheme represents common conversions between formal external API versions
 | 
				
			||||||
 | 
					// and the internal Go structs. ObjectScheme is typically used with ObjectCodec to
 | 
				
			||||||
 | 
					// transform internal Go structs into serialized versions. There may be many valid
 | 
				
			||||||
 | 
					// ObjectCodecs for each ObjectScheme.
 | 
				
			||||||
 | 
					type ObjectScheme interface {
 | 
				
			||||||
 | 
						ObjectConvertor
 | 
				
			||||||
 | 
						ObjectTyper
 | 
				
			||||||
 | 
						ObjectCreater
 | 
				
			||||||
 | 
						ObjectCopier
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ObjectCodec represents the common mechanisms for converting to and from a particular
 | 
				
			||||||
 | 
					// binary representation of an object.
 | 
				
			||||||
 | 
					type ObjectCodec interface {
 | 
				
			||||||
 | 
						ObjectEncoder
 | 
				
			||||||
 | 
						Decoder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Decoder defines methods for deserializing API objects into a given type
 | 
					// Decoder defines methods for deserializing API objects into a given type
 | 
				
			||||||
type Decoder interface {
 | 
					type Decoder interface {
 | 
				
			||||||
	Decode(data []byte) (Object, error)
 | 
						Decode(data []byte) (Object, error)
 | 
				
			||||||
@@ -33,6 +51,22 @@ type Codec interface {
 | 
				
			|||||||
	Encoder
 | 
						Encoder
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ObjectCopier duplicates an object.
 | 
				
			||||||
 | 
					type ObjectCopier interface {
 | 
				
			||||||
 | 
						// Copy returns an exact copy of the provided Object, or an error if the
 | 
				
			||||||
 | 
						// copy could not be completed.
 | 
				
			||||||
 | 
						Copy(Object) (Object, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ObjectEncoder turns an object into a byte array. This interface is a
 | 
				
			||||||
 | 
					// general form of the Encoder interface
 | 
				
			||||||
 | 
					type ObjectEncoder interface {
 | 
				
			||||||
 | 
						// EncodeToVersion convert and serializes an object in the internal format
 | 
				
			||||||
 | 
						// to a specified output version. An error is returned if the object
 | 
				
			||||||
 | 
						// cannot be converted for any reason.
 | 
				
			||||||
 | 
						EncodeToVersion(obj Object, outVersion string) ([]byte, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ObjectConvertor converts an object to a different version.
 | 
					// ObjectConvertor converts an object to a different version.
 | 
				
			||||||
type ObjectConvertor interface {
 | 
					type ObjectConvertor interface {
 | 
				
			||||||
	Convert(in, out interface{}) error
 | 
						Convert(in, out interface{}) error
 | 
				
			||||||
@@ -43,8 +77,17 @@ type ObjectConvertor interface {
 | 
				
			|||||||
// ObjectTyper contains methods for extracting the APIVersion and Kind
 | 
					// ObjectTyper contains methods for extracting the APIVersion and Kind
 | 
				
			||||||
// of objects.
 | 
					// of objects.
 | 
				
			||||||
type ObjectTyper interface {
 | 
					type ObjectTyper interface {
 | 
				
			||||||
 | 
						// DataVersionAndKind returns the version and kind of the provided data, or an error
 | 
				
			||||||
 | 
						// if another problem is detected. In many cases this method can be as expensive to
 | 
				
			||||||
 | 
						// invoke as the Decode method.
 | 
				
			||||||
	DataVersionAndKind([]byte) (version, kind string, err error)
 | 
						DataVersionAndKind([]byte) (version, kind string, err error)
 | 
				
			||||||
 | 
						// ObjectVersionAndKind returns the version and kind of the provided object, or an
 | 
				
			||||||
 | 
						// error if the object is not recognized (IsNotRegisteredError will return true).
 | 
				
			||||||
	ObjectVersionAndKind(Object) (version, kind string, err error)
 | 
						ObjectVersionAndKind(Object) (version, kind string, err error)
 | 
				
			||||||
 | 
						// Recognizes returns true if the scheme is able to handle the provided version and kind,
 | 
				
			||||||
 | 
						// or more precisely that the provided version is a possible conversion or decoding
 | 
				
			||||||
 | 
						// target.
 | 
				
			||||||
 | 
						Recognizes(version, kind string) bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ObjectCreater contains methods for instantiating an object by kind and version.
 | 
					// ObjectCreater contains methods for instantiating an object by kind and version.
 | 
				
			||||||
@@ -52,6 +95,20 @@ type ObjectCreater interface {
 | 
				
			|||||||
	New(version, kind string) (out Object, err error)
 | 
						New(version, kind string) (out Object, err error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ObjectDecoder is a convenience interface for identifying serialized versions of objects
 | 
				
			||||||
 | 
					// and transforming them into Objects. It intentionally overlaps with ObjectTyper and
 | 
				
			||||||
 | 
					// Decoder for use in decode only paths.
 | 
				
			||||||
 | 
					type ObjectDecoder interface {
 | 
				
			||||||
 | 
						Decoder
 | 
				
			||||||
 | 
						// DataVersionAndKind returns the version and kind of the provided data, or an error
 | 
				
			||||||
 | 
						// if another problem is detected. In many cases this method can be as expensive to
 | 
				
			||||||
 | 
						// invoke as the Decode method.
 | 
				
			||||||
 | 
						DataVersionAndKind([]byte) (version, kind string, err error)
 | 
				
			||||||
 | 
						// Recognizes returns true if the scheme is able to handle the provided version and kind
 | 
				
			||||||
 | 
						// of an object.
 | 
				
			||||||
 | 
						Recognizes(version, kind string) bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ResourceVersioner provides methods for setting and retrieving
 | 
					// ResourceVersioner provides methods for setting and retrieving
 | 
				
			||||||
// the resource version from an API object.
 | 
					// the resource version from an API object.
 | 
				
			||||||
type ResourceVersioner interface {
 | 
					type ResourceVersioner interface {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ limitations under the License.
 | 
				
			|||||||
package runtime
 | 
					package runtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
@@ -164,7 +165,15 @@ func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExt
 | 
				
			|||||||
	for i := range src {
 | 
						for i := range src {
 | 
				
			||||||
		switch t := src[i].(type) {
 | 
							switch t := src[i].(type) {
 | 
				
			||||||
		case *Unknown:
 | 
							case *Unknown:
 | 
				
			||||||
 | 
								// TODO: this should be decoupled from the scheme (since it is JSON specific)
 | 
				
			||||||
			dest[i].RawJSON = t.RawJSON
 | 
								dest[i].RawJSON = t.RawJSON
 | 
				
			||||||
 | 
							case *Unstructured:
 | 
				
			||||||
 | 
								// TODO: this should be decoupled from the scheme (since it is JSON specific)
 | 
				
			||||||
 | 
								data, err := json.Marshal(t.Object)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								dest[i].RawJSON = data
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			version := outVersion
 | 
								version := outVersion
 | 
				
			||||||
			// if the object exists
 | 
								// if the object exists
 | 
				
			||||||
@@ -192,16 +201,11 @@ func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for i := range src {
 | 
						for i := range src {
 | 
				
			||||||
		data := src[i].RawJSON
 | 
							data := src[i].RawJSON
 | 
				
			||||||
		obj, err := scheme.Decode(data)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if !IsNotRegisteredError(err) {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		version, kind, err := scheme.raw.DataVersionAndKind(data)
 | 
							version, kind, err := scheme.raw.DataVersionAndKind(data)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
			obj = &Unknown{
 | 
							dest[i] = &Unknown{
 | 
				
			||||||
			TypeMeta: TypeMeta{
 | 
								TypeMeta: TypeMeta{
 | 
				
			||||||
				APIVersion: version,
 | 
									APIVersion: version,
 | 
				
			||||||
				Kind:       kind,
 | 
									Kind:       kind,
 | 
				
			||||||
@@ -209,8 +213,6 @@ func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[]
 | 
				
			|||||||
			RawJSON: data,
 | 
								RawJSON: data,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		dest[i] = obj
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	*out = dest
 | 
						*out = dest
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -275,6 +277,12 @@ func (s *Scheme) ObjectVersionAndKind(obj Object) (version, kind string, err err
 | 
				
			|||||||
	return s.raw.ObjectVersionAndKind(obj)
 | 
						return s.raw.ObjectVersionAndKind(obj)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Recognizes returns true if the scheme is able to handle the provided version and kind
 | 
				
			||||||
 | 
					// of an object.
 | 
				
			||||||
 | 
					func (s *Scheme) Recognizes(version, kind string) bool {
 | 
				
			||||||
 | 
						return s.raw.Recognizes(version, kind)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New returns a new API object of the given version ("" for internal
 | 
					// New returns a new API object of the given version ("" for internal
 | 
				
			||||||
// representation) and name, or an error if it hasn't been registered.
 | 
					// representation) and name, or an error if it hasn't been registered.
 | 
				
			||||||
func (s *Scheme) New(versionName, typeName string) (Object, error) {
 | 
					func (s *Scheme) New(versionName, typeName string) (Object, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,3 +116,17 @@ type Unknown struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (*Unknown) IsAnAPIObject() {}
 | 
					func (*Unknown) IsAnAPIObject() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Unstructured allows objects that do not have Golang structs registered to be manipulated
 | 
				
			||||||
 | 
					// generically. This can be used to deal with the API objects from a plug-in. Unstructured
 | 
				
			||||||
 | 
					// objects still have functioning TypeMeta features-- kind, version, etc.
 | 
				
			||||||
 | 
					// TODO: Make this object have easy access to field based accessors and settors for
 | 
				
			||||||
 | 
					// metadata and field mutatation.
 | 
				
			||||||
 | 
					type Unstructured struct {
 | 
				
			||||||
 | 
						TypeMeta `json:",inline"`
 | 
				
			||||||
 | 
						// Object is a JSON compatible map with string, float, int, []interface{}, or map[string]interface{}
 | 
				
			||||||
 | 
						// children.
 | 
				
			||||||
 | 
						Object map[string]interface{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*Unstructured) IsAnAPIObject() {}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										89
									
								
								pkg/runtime/unstructured.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								pkg/runtime/unstructured.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2015 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package runtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
 | 
				
			||||||
 | 
					// type, which can be used for generic access to objects without a predefined scheme.
 | 
				
			||||||
 | 
					var UnstructuredJSONScheme ObjectDecoder = unstructuredJSONScheme{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type unstructuredJSONScheme struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Recognizes returns true for any version or kind that is specified (internal
 | 
				
			||||||
 | 
					// versions are specifically excluded).
 | 
				
			||||||
 | 
					func (unstructuredJSONScheme) Recognizes(version, kind string) bool {
 | 
				
			||||||
 | 
						return len(version) > 0 && len(kind) > 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s unstructuredJSONScheme) Decode(data []byte) (Object, error) {
 | 
				
			||||||
 | 
						unstruct := &Unstructured{}
 | 
				
			||||||
 | 
						if err := s.DecodeInto(data, unstruct); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return unstruct, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error {
 | 
				
			||||||
 | 
						unstruct, ok := obj.(*Unstructured)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("the unstructured JSON scheme does not recognize %v", reflect.TypeOf(obj))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m := make(map[string]interface{})
 | 
				
			||||||
 | 
						if err := json.Unmarshal(data, &m); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v, ok := m["kind"]; ok {
 | 
				
			||||||
 | 
							if s, ok := v.(string); ok {
 | 
				
			||||||
 | 
								unstruct.Kind = s
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v, ok := m["apiVersion"]; ok {
 | 
				
			||||||
 | 
							if s, ok := v.(string); ok {
 | 
				
			||||||
 | 
								unstruct.APIVersion = s
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(unstruct.APIVersion) == 0 {
 | 
				
			||||||
 | 
							return conversion.NewMissingVersionErr(string(data))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(unstruct.Kind) == 0 {
 | 
				
			||||||
 | 
							return conversion.NewMissingKindErr(string(data))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						unstruct.Object = m
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (unstructuredJSONScheme) DataVersionAndKind(data []byte) (version, kind string, err error) {
 | 
				
			||||||
 | 
						obj := TypeMeta{}
 | 
				
			||||||
 | 
						if err := json.Unmarshal(data, &obj); err != nil {
 | 
				
			||||||
 | 
							return "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(obj.APIVersion) == 0 {
 | 
				
			||||||
 | 
							return "", "", conversion.NewMissingVersionErr(string(data))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(obj.Kind) == 0 {
 | 
				
			||||||
 | 
							return "", "", conversion.NewMissingKindErr(string(data))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return obj.APIVersion, obj.Kind, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								pkg/runtime/unstructured_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								pkg/runtime/unstructured_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package runtime_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDecodeUnstructured(t *testing.T) {
 | 
				
			||||||
 | 
						pl := &api.List{
 | 
				
			||||||
 | 
							Items: []runtime.Object{
 | 
				
			||||||
 | 
								&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
								&runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: "v1beta3"}, RawJSON: []byte(`{"kind":"Pod","apiVersion":"v1beta3","metadata":{"name":"test"}}`)},
 | 
				
			||||||
 | 
								&runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "", APIVersion: "v1beta3"}, RawJSON: []byte(`{"kind":"Pod","apiVersion":"v1beta3","metadata":{"name":"test"}}`)},
 | 
				
			||||||
 | 
								&runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if errs := runtime.DecodeList(pl.Items, runtime.UnstructuredJSONScheme); len(errs) == 1 {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error %v", errs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if pod, ok := pl.Items[1].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
 | 
				
			||||||
 | 
							t.Errorf("object not converted: %#v", pl.Items[1])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := pl.Items[2].(*runtime.Unknown); !ok {
 | 
				
			||||||
 | 
							t.Errorf("object should not have been converted: %#v", pl.Items[2])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -153,7 +153,7 @@ func TestNewBuilder(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, item := range tests {
 | 
						for _, item := range tests {
 | 
				
			||||||
		o := testclient.NewObjects(api.Scheme)
 | 
							o := testclient.NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
		o.Add(item.pv)
 | 
							o.Add(item.pv)
 | 
				
			||||||
		o.Add(item.claim)
 | 
							o.Add(item.claim)
 | 
				
			||||||
		client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
 | 
							client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
 | 
				
			||||||
@@ -213,7 +213,7 @@ func TestNewBuilderClaimNotBound(t *testing.T) {
 | 
				
			|||||||
			ClaimName: "claimC",
 | 
								ClaimName: "claimC",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	o := testclient.NewObjects(api.Scheme)
 | 
						o := testclient.NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
	o.Add(pv)
 | 
						o.Add(pv)
 | 
				
			||||||
	o.Add(claim)
 | 
						o.Add(claim)
 | 
				
			||||||
	client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
 | 
						client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRunStop(t *testing.T) {
 | 
					func TestRunStop(t *testing.T) {
 | 
				
			||||||
	o := testclient.NewObjects(api.Scheme)
 | 
						o := testclient.NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
	client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
 | 
						client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}
 | 
				
			||||||
	binder := NewPersistentVolumeClaimBinder(client, 1*time.Second)
 | 
						binder := NewPersistentVolumeClaimBinder(client, 1*time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -111,8 +111,8 @@ func TestExampleObjects(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for name, scenario := range scenarios {
 | 
						for name, scenario := range scenarios {
 | 
				
			||||||
		o := testclient.NewObjects(api.Scheme)
 | 
							o := testclient.NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
		if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/"+name, o); err != nil {
 | 
							if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/"+name, o, api.Scheme); err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -168,11 +168,11 @@ func TestExampleObjects(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestBindingWithExamples(t *testing.T) {
 | 
					func TestBindingWithExamples(t *testing.T) {
 | 
				
			||||||
	api.ForTesting_ReferencesAllowBlankSelfLinks = true
 | 
						api.ForTesting_ReferencesAllowBlankSelfLinks = true
 | 
				
			||||||
	o := testclient.NewObjects(api.Scheme)
 | 
						o := testclient.NewObjects(api.Scheme, api.Scheme)
 | 
				
			||||||
	if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/claims/claim-01.yaml", o); err != nil {
 | 
						if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/claims/claim-01.yaml", o, api.Scheme); err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/volumes/local-01.yaml", o); err != nil {
 | 
						if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/volumes/local-01.yaml", o, api.Scheme); err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user