mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Allow conversion between []runtime.Object and []runtime.RawExtension
This allows generic lists with unrecognized objects to be roundtripped between internal and external objects.
This commit is contained in:
		@@ -22,16 +22,14 @@ import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
			
		||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var scheme = runtime.NewScheme()
 | 
			
		||||
var Codec = runtime.CodecFor(scheme, "v1test")
 | 
			
		||||
 | 
			
		||||
type EmbeddedTest struct {
 | 
			
		||||
	runtime.TypeMeta `json:",inline"`
 | 
			
		||||
	ID               string                 `json:"id,omitempty"`
 | 
			
		||||
	Object           runtime.EmbeddedObject `json:"object,omitempty"`
 | 
			
		||||
	EmptyObject      runtime.EmbeddedObject `json:"emptyObject,omitempty"`
 | 
			
		||||
	runtime.TypeMeta
 | 
			
		||||
	ID          string
 | 
			
		||||
	Object      runtime.EmbeddedObject
 | 
			
		||||
	EmptyObject runtime.EmbeddedObject
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type EmbeddedTestExternal struct {
 | 
			
		||||
@@ -41,21 +39,91 @@ type EmbeddedTestExternal struct {
 | 
			
		||||
	EmptyObject      runtime.RawExtension `json:"emptyObject,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ObjectTest struct {
 | 
			
		||||
	runtime.TypeMeta
 | 
			
		||||
 | 
			
		||||
	ID    string
 | 
			
		||||
	Items []runtime.Object
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ObjectTestExternal struct {
 | 
			
		||||
	runtime.TypeMeta `yaml:",inline" json:",inline"`
 | 
			
		||||
 | 
			
		||||
	ID    string                 `json:"id,omitempty"`
 | 
			
		||||
	Items []runtime.RawExtension `json:"items,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*ObjectTest) IsAnAPIObject()           {}
 | 
			
		||||
func (*ObjectTestExternal) IsAnAPIObject()   {}
 | 
			
		||||
func (*EmbeddedTest) IsAnAPIObject()         {}
 | 
			
		||||
func (*EmbeddedTestExternal) IsAnAPIObject() {}
 | 
			
		||||
 | 
			
		||||
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
 | 
			
		||||
	s := runtime.NewScheme()
 | 
			
		||||
	s.AddKnownTypes("", &ObjectTest{})
 | 
			
		||||
	s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
 | 
			
		||||
 | 
			
		||||
	_, err := s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{}]}`))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatalf("unexpected non-error")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestArrayOfRuntimeObject(t *testing.T) {
 | 
			
		||||
	s := runtime.NewScheme()
 | 
			
		||||
	s.AddKnownTypes("", &EmbeddedTest{})
 | 
			
		||||
	s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{})
 | 
			
		||||
	s.AddKnownTypes("", &ObjectTest{})
 | 
			
		||||
	s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
 | 
			
		||||
 | 
			
		||||
	internal := &ObjectTest{
 | 
			
		||||
		Items: []runtime.Object{
 | 
			
		||||
			&EmbeddedTest{ID: "foo"},
 | 
			
		||||
			&EmbeddedTest{ID: "bar"},
 | 
			
		||||
			// TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
 | 
			
		||||
			&runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown","foo":"bar","kind":"OtherTest"}`)},
 | 
			
		||||
			&ObjectTest{
 | 
			
		||||
				Items: []runtime.Object{
 | 
			
		||||
					&EmbeddedTest{ID: "baz"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	wire, err := s.EncodeToVersion(internal, "v1test")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Logf("Wire format is:\n%s\n", string(wire))
 | 
			
		||||
 | 
			
		||||
	obj := &ObjectTestExternal{}
 | 
			
		||||
	if err := json.Unmarshal(wire, obj); err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Logf("exact wire is: %#v", string(obj.Items[0].RawJSON))
 | 
			
		||||
 | 
			
		||||
	decoded, err := s.Decode(wire)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	internal.Items[2].(*runtime.Unknown).Kind = "OtherTest"
 | 
			
		||||
	internal.Items[2].(*runtime.Unknown).APIVersion = "unknown"
 | 
			
		||||
	if e, a := internal, decoded; !reflect.DeepEqual(e, a) {
 | 
			
		||||
		t.Log(string(decoded.(*ObjectTest).Items[2].(*runtime.Unknown).RawJSON))
 | 
			
		||||
		t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestEmbeddedObject(t *testing.T) {
 | 
			
		||||
	s := scheme
 | 
			
		||||
	s := runtime.NewScheme()
 | 
			
		||||
	s.AddKnownTypes("", &EmbeddedTest{})
 | 
			
		||||
	s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{})
 | 
			
		||||
 | 
			
		||||
	outer := &EmbeddedTest{
 | 
			
		||||
		TypeMeta: runtime.TypeMeta{},
 | 
			
		||||
		ID:       "outer",
 | 
			
		||||
		ID: "outer",
 | 
			
		||||
		Object: runtime.EmbeddedObject{
 | 
			
		||||
			&EmbeddedTest{
 | 
			
		||||
				TypeMeta: runtime.TypeMeta{},
 | 
			
		||||
				ID:       "inner",
 | 
			
		||||
				ID: "inner",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -140,15 +140,77 @@ func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *Embedded
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// runtimeObjectToRawExtensionArray takes a list of objects and encodes them as RawExtension in the output version
 | 
			
		||||
// defined by the conversion.Scope. If objects must be encoded to different schema versions you should set them as
 | 
			
		||||
// runtime.Unknown in the internal version instead.
 | 
			
		||||
func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExtension, s conversion.Scope) error {
 | 
			
		||||
	src := *in
 | 
			
		||||
	dest := make([]RawExtension, len(src))
 | 
			
		||||
 | 
			
		||||
	_, outVersion, scheme := self.fromScope(s)
 | 
			
		||||
 | 
			
		||||
	for i := range src {
 | 
			
		||||
		switch t := src[i].(type) {
 | 
			
		||||
		case *Unknown:
 | 
			
		||||
			dest[i].RawJSON = t.RawJSON
 | 
			
		||||
		default:
 | 
			
		||||
			data, err := scheme.EncodeToVersion(src[i], outVersion)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			dest[i].RawJSON = data
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	*out = dest
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rawExtensionToRuntimeObjectArray attempts to decode objects from the array - if they are unrecognized objects,
 | 
			
		||||
// they are added as Unknown.
 | 
			
		||||
func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[]Object, s conversion.Scope) error {
 | 
			
		||||
	src := *in
 | 
			
		||||
	dest := make([]Object, len(src))
 | 
			
		||||
 | 
			
		||||
	_, _, scheme := self.fromScope(s)
 | 
			
		||||
 | 
			
		||||
	for i := range src {
 | 
			
		||||
		data := src[i].RawJSON
 | 
			
		||||
		obj, err := scheme.Decode(data)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if !IsNotRegisteredError(err) {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			version, kind, err := scheme.raw.DataVersionAndKind(data)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			obj = &Unknown{
 | 
			
		||||
				TypeMeta: TypeMeta{
 | 
			
		||||
					APIVersion: version,
 | 
			
		||||
					Kind:       kind,
 | 
			
		||||
				},
 | 
			
		||||
				RawJSON: data,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		dest[i] = obj
 | 
			
		||||
	}
 | 
			
		||||
	*out = dest
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewScheme creates a new Scheme. This scheme is pluggable by default.
 | 
			
		||||
func NewScheme() *Scheme {
 | 
			
		||||
	s := &Scheme{conversion.NewScheme()}
 | 
			
		||||
	s.raw.InternalVersion = ""
 | 
			
		||||
	s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"}
 | 
			
		||||
	s.raw.AddConversionFuncs(
 | 
			
		||||
	if err := s.raw.AddConversionFuncs(
 | 
			
		||||
		s.embeddedObjectToRawExtension,
 | 
			
		||||
		s.rawExtensionToEmbeddedObject,
 | 
			
		||||
	)
 | 
			
		||||
		s.runtimeObjectToRawExtensionArray,
 | 
			
		||||
		s.rawExtensionToRuntimeObjectArray,
 | 
			
		||||
	); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user