// NewStorageCodec assembles a storage codec for the provided storage media type, the provided serializer, and the requested // storage and memory versions. func NewStorageCodec(storageMediaType string, ns runtime.StorageSerializer, storageVersion, memoryVersion unversioned.GroupVersion, config storagebackend.Config) (runtime.Codec, error) { mediaType, _, err := mime.ParseMediaType(storageMediaType) if err != nil { return nil, fmt.Errorf("%q is not a valid mime-type", storageMediaType) } serializer, ok := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), mediaType) if !ok { return nil, fmt.Errorf("unable to find serializer for %q", storageMediaType) } s := serializer.Serializer // etcd2 only supports string data - we must wrap any result before returning // TODO: storagebackend should return a boolean indicating whether it supports binary data if !serializer.EncodesAsText && (config.Type == storagebackend.StorageTypeUnset || config.Type == storagebackend.StorageTypeETCD2) { glog.V(4).Infof("Wrapping the underlying binary storage serializer with a base64 encoding for etcd2") s = runtime.NewBase64Serializer(s) } encoder := ns.EncoderForVersion( s, runtime.NewMultiGroupVersioner( storageVersion, unversioned.GroupKind{Group: storageVersion.Group}, unversioned.GroupKind{Group: memoryVersion.Group}, ), ) ds := recognizer.NewDecoder(s, ns.UniversalDeserializer()) decoder := ns.DecoderToVersion( ds, runtime.NewMultiGroupVersioner( memoryVersion, unversioned.GroupKind{Group: memoryVersion.Group}, unversioned.GroupKind{Group: storageVersion.Group}, ), ) return runtime.NewCodec(encoder, decoder), nil }
func TestSetControllerConversion(t *testing.T) { if err := api.Scheme.AddConversionFuncs(Convert_v1beta1_ReplicaSet_to_api_ReplicationController); err != nil { t.Fatal(err) } rs := &extensions.ReplicaSet{} rc := &api.ReplicationController{} extGroup := testapi.Extensions defaultGroup := testapi.Default fuzzInternalObject(t, extGroup.InternalGroupVersion(), rs, rand.Int63()) t.Logf("rs._internal.extensions -> rs.v1beta1.extensions") data, err := runtime.Encode(extGroup.Codec(), rs) if err != nil { t.Fatalf("unexpected encoding error: %v", err) } decoder := api.Codecs.DecoderToVersion( api.Codecs.UniversalDeserializer(), runtime.NewMultiGroupVersioner( *defaultGroup.GroupVersion(), unversioned.GroupKind{Group: defaultGroup.GroupVersion().Group}, unversioned.GroupKind{Group: extGroup.GroupVersion().Group}, ), ) t.Logf("rs.v1beta1.extensions -> rc._internal") if err := runtime.DecodeInto(decoder, data, rc); err != nil { t.Fatalf("unexpected decoding error: %v", err) } t.Logf("rc._internal -> rc.v1") data, err = runtime.Encode(defaultGroup.Codec(), rc) if err != nil { t.Fatalf("unexpected encoding error: %v", err) } t.Logf("rc.v1 -> rs._internal.extensions") if err := runtime.DecodeInto(decoder, data, rs); err != nil { t.Fatalf("unexpected decoding error: %v", err) } }
func TestConvertToVersion(t *testing.T) { testCases := []struct { scheme *runtime.Scheme in runtime.Object gv runtime.GroupVersioner same bool out runtime.Object errFn func(error) bool }{ // errors if the type is not registered in the scheme { scheme: GetTestScheme(), in: &UnknownType{}, errFn: func(err error) bool { return err != nil && runtime.IsNotRegisteredError(err) }, }, // errors if the group versioner returns no target { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: testGroupVersioner{}, errFn: func(err error) bool { return err != nil && strings.Contains(err.Error(), "is not suitable for converting") }, }, // converts to internal { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: schema.GroupVersion{Version: "__internal"}, out: &TestType1{A: "test"}, }, // prefers the best match { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: schema.GroupVersions{{Version: "__internal"}, {Version: "v1"}}, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"}, A: "test", }, }, // unversioned type returned as-is { scheme: GetTestScheme(), in: &UnversionedType{A: "test"}, gv: schema.GroupVersions{{Version: "v1"}}, same: true, out: &UnversionedType{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"}, A: "test", }, }, // unversioned type returned when not included in the target types { scheme: GetTestScheme(), in: &UnversionedType{A: "test"}, gv: schema.GroupVersions{{Group: "other", Version: "v2"}}, same: true, out: &UnversionedType{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"}, A: "test", }, }, // detected as already being in the target version { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: schema.GroupVersions{{Version: "v1"}}, same: true, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"}, A: "test", }, }, // detected as already being in the first target version { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: schema.GroupVersions{{Version: "v1"}, {Version: "__internal"}}, same: true, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"}, A: "test", }, }, // detected as already being in the first target version { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: schema.GroupVersions{{Version: "v1"}, {Version: "__internal"}}, same: true, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"}, A: "test", }, }, // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (1/3): different kind { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType3", Version: "v1"}}, same: true, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"}, A: "test", }, }, // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (2/3): different gv { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType3", Group: "custom", Version: "v1"}}, same: true, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType3"}, A: "test", }, }, // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (3/3): different gvk { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Group: "custom", Version: "v1", Kind: "TestType5"}}, same: true, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"}, A: "test", }, }, // multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "other", Version: "v2"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}, schema.GroupKind{Kind: "TestType1"}), out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"}, A: "test", }, }, // multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "other", Version: "v2"}, schema.GroupKind{Kind: "TestType1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}), out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"}, A: "test", }, }, // multi group versioner is unable to find a match when kind AND group don't match (there is no TestType1 kind in group "other", and no kind "TestType5" in the default group) { scheme: GetTestScheme(), in: &TestType1{A: "test"}, gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "custom", Version: "v1"}, schema.GroupKind{Group: "other"}, schema.GroupKind{Kind: "TestType5"}), errFn: func(err error) bool { return err != nil && strings.Contains(err.Error(), "is not suitable for converting") }, }, // multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "", Version: "v1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}, schema.GroupKind{Kind: "TestType1"}), same: true, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"}, A: "test", }, }, // multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy { scheme: GetTestScheme(), in: &ExternalTestType1{A: "test"}, gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "", Version: "v1"}, schema.GroupKind{Kind: "TestType1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}), same: true, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"}, A: "test", }, }, // group versioner can choose a particular target kind for a given input when kind is the same across group versions { scheme: GetTestScheme(), in: &TestType1{A: "test"}, gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Version: "v1", Kind: "TestType3"}}, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"}, A: "test", }, }, // group versioner can choose a different kind { scheme: GetTestScheme(), in: &TestType1{A: "test"}, gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType5", Group: "custom", Version: "v1"}}, out: &ExternalTestType1{ MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"}, A: "test", }, }, } for i, test := range testCases { original, _ := test.scheme.DeepCopy(test.in) out, err := test.scheme.ConvertToVersion(test.in, test.gv) switch { case test.errFn != nil: if !test.errFn(err) { t.Errorf("%d: unexpected error: %v", i, err) } continue case err != nil: t.Errorf("%d: unexpected error: %v", i, err) continue } if out == test.in { t.Errorf("%d: ConvertToVersion should always copy out: %#v", i, out) continue } if test.same { if !reflect.DeepEqual(original, test.in) { t.Errorf("%d: unexpected mutation of input: %s", i, diff.ObjectReflectDiff(original, test.in)) continue } if !reflect.DeepEqual(out, test.out) { t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out)) continue } unsafe, err := test.scheme.UnsafeConvertToVersion(test.in, test.gv) if err != nil { t.Errorf("%d: unexpected error: %v", i, err) continue } if !reflect.DeepEqual(unsafe, test.out) { t.Errorf("%d: unexpected unsafe: %s", i, diff.ObjectReflectDiff(unsafe, test.out)) continue } if unsafe != test.in { t.Errorf("%d: UnsafeConvertToVersion should return same object: %#v", i, unsafe) continue } continue } if !reflect.DeepEqual(out, test.out) { t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out)) continue } } }