func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} externalGVK := externalGV.WithKind("ObjectTest") s := runtime.NewScheme() s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{}) codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } test := obj.(*ObjectTest) if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != "{}" || unk.ContentType != runtime.ContentTypeJSON { t.Fatalf("unexpected object: %#v", test.Items[0]) } if *gvk != externalGVK { t.Fatalf("unexpected kind: %#v", gvk) } obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } test = obj.(*ObjectTest) if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != `{"kind":"Other","apiVersion":"v1"}` || unk.ContentType != runtime.ContentTypeJSON { t.Fatalf("unexpected object: %#v", test.Items[0]) } if *gvk != externalGVK { t.Fatalf("unexpected kind: %#v", gvk) } }
func TestRESTMapper(t *testing.T) { gv := unversioned.GroupVersion{Group: componentconfig.GroupName, Version: "v1alpha1"} proxyGVK := gv.WithKind("KubeProxyConfiguration") if gvk, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.KindFor(gv.WithResource("kubeproxyconfiguration")); err != nil || gvk != proxyGVK { t.Errorf("unexpected version mapping: %v %v", gvk, err) } if m, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.RESTMapping(proxyGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != proxyGVK || m.Resource != "kubeproxyconfigurations" { t.Errorf("unexpected version mapping: %#v %v", m, err) } for _, version := range registered.GroupOrDie(componentconfig.GroupName).GroupVersions { mapping, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.RESTMapping(proxyGVK.GroupKind(), version.Version) if err != nil { t.Errorf("unexpected error: %v", err) continue } if mapping.Resource != "kubeproxyconfigurations" { t.Errorf("incorrect resource name: %#v", mapping) } if mapping.GroupVersionKind.GroupVersion() != version { t.Errorf("incorrect groupVersion: %v", mapping) } interfaces, _ := registered.GroupOrDie(componentconfig.GroupName).InterfacesFor(version) if mapping.ObjectConvertor != interfaces.ObjectConvertor { t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) } } }
func TestEncode(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) obj2, gvk, err2 := codec.Decode(data, nil, nil) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) } if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) { t.Errorf("unexpected gvk returned by decode: %#v", gvk) } }
func TestExternalToInternalMapping(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) table := []struct { obj runtime.Object encoded string }{ { &InternalOptionalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}}, `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } for _, item := range table { gotDecoded, err := runtime.Decode(scheme, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { var eEx, aEx runtime.Object if obj, ok := e.(*InternalOptionalExtensionType); ok { eEx = obj.Extension.Object } if obj, ok := a.(*InternalOptionalExtensionType); ok { aEx = obj.Extension.Object } t.Errorf("expected %#v, got %#v (%#v, %#v)", e, a, eEx, aEx) } } }
// DecodeToVersion converts a JSON string back into a pointer to an api object. // Deduces the type based upon the fields added by the MetaInsertionFactory // technique. The object will be converted, if necessary, into the versioned // type before being returned. Decode will not decode objects without version // set unless version is also "". // a GroupVersion with .IsEmpty() == true is means "use the internal version for // the object's group" func (s *Scheme) DecodeToVersion(data []byte, targetVersion unversioned.GroupVersion) (interface{}, error) { obj, sourceKind, err := s.DecodeToVersionedObject(data) if err != nil { return nil, err } // Version and Kind should be blank in memory. if err := s.SetVersionAndKind("", "", obj); err != nil { return nil, err } // if the targetVersion is empty, then we want the internal version, but the internal version varies by // group. We can lookup the group now because we have knowledge of the group if targetVersion.IsEmpty() { exists := false targetVersion, exists = s.InternalVersions[sourceKind.Group] if !exists { return nil, fmt.Errorf("no internalVersion specified for %v", targetVersion) } } // Convert if needed. if targetVersion != sourceKind.GroupVersion() { objOut, err := s.NewObject(targetVersion.WithKind(sourceKind.Kind)) if err != nil { return nil, err } flags, meta := s.generateConvertMeta(sourceKind.GroupVersion(), targetVersion, obj) if err := s.converter.Convert(obj, objOut, flags, meta); err != nil { return nil, err } obj = objOut } return obj, nil }
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} externalGVK := externalGV.WithKind("ObjectTest") s := runtime.NewScheme() s.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{}) obj, err := s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","items":[{}]}`)) if err != nil { 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":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","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]) } }
func TestEncode(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) codec := runtime.CodecFor(scheme, externalGV.String()) test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) obj2, err2 := runtime.Decode(codec, data) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", &test, obj2) } }
func TestKindToResource(t *testing.T) { testCases := []struct { Kind string Plural, Singular string }{ {Kind: "Pod", Plural: "pods", Singular: "pod"}, {Kind: "ReplicationController", Plural: "replicationcontrollers", Singular: "replicationcontroller"}, // Add "ies" when ending with "y" {Kind: "ImageRepository", Plural: "imagerepositories", Singular: "imagerepository"}, // Add "es" when ending with "s" {Kind: "miss", Plural: "misses", Singular: "miss"}, // Add "s" otherwise {Kind: "lowercase", Plural: "lowercases", Singular: "lowercase"}, } for i, testCase := range testCases { version := unversioned.GroupVersion{} plural, singular := KindToResource(version.WithKind(testCase.Kind)) if singular != version.WithResource(testCase.Singular) || plural != version.WithResource(testCase.Plural) { t.Errorf("%d: unexpected plural and singular: %v %v", i, plural, singular) } } }
func TestRESTMapperResourceSingularizer(t *testing.T) { testGroupVersion := unversioned.GroupVersion{Group: "tgroup", Version: "test"} testCases := []struct { Kind string Plural string Singular string }{ {Kind: "Pod", Plural: "pods", Singular: "pod"}, {Kind: "ReplicationController", Plural: "replicationcontrollers", Singular: "replicationcontroller"}, {Kind: "ImageRepository", Plural: "imagerepositories", Singular: "imagerepository"}, {Kind: "Status", Plural: "statuses", Singular: "status"}, {Kind: "lowercase", Plural: "lowercases", Singular: "lowercase"}, // TODO this test is broken. This updates to reflect actual behavior. Kinds are expected to be singular // old (incorrect), coment: Don't add extra s if the original object is already plural {Kind: "lowercases", Plural: "lowercaseses", Singular: "lowercases"}, } for i, testCase := range testCases { mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{testGroupVersion}, fakeInterfaces) // create singular/plural mapping mapper.Add(testGroupVersion.WithKind(testCase.Kind), RESTScopeNamespace) singular, err := mapper.ResourceSingularizer(testCase.Plural) if err != nil { t.Errorf("%d: unexpected error: %v", i, err) } if singular != testCase.Singular { t.Errorf("%d: mismatched singular: got %v, expected %v", i, singular, testCase.Singular) } } }
func TestRESTMapper(t *testing.T) { gv := unversioned.GroupVersion{Group: "componentconfig", Version: "v1alpha1"} proxyGVK := gv.WithKind("KubeProxyConfiguration") if gvk, err := latest.GroupOrDie("componentconfig").RESTMapper.KindFor("kubeproxyconfiguration"); err != nil || gvk != proxyGVK { t.Errorf("unexpected version mapping: %v %v", gvk, err) } if m, err := latest.GroupOrDie("componentconfig").RESTMapper.RESTMapping(proxyGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != proxyGVK || m.Resource != "kubeproxyconfigurations" { t.Errorf("unexpected version mapping: %#v %v", m, err) } for _, version := range latest.GroupOrDie("componentconfig").Versions { mapping, err := latest.GroupOrDie("componentconfig").RESTMapper.RESTMapping(proxyGVK.GroupKind(), version) if err != nil { t.Errorf("unexpected error: %v", err) continue } if mapping.Resource != "kubeproxyconfigurations" { t.Errorf("incorrect resource name: %#v", mapping) } if mapping.GroupVersionKind.GroupVersion() != gv { t.Errorf("incorrect groupVersion: %v", mapping) } interfaces, _ := latest.GroupOrDie("componentconfig").InterfacesFor(gv.String()) if mapping.Codec != interfaces.Codec { t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) } } }
func TestGenerateConversionsForStruct(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"} scheme := NewScheme() scheme.Log(t) scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &Internal{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &External{}) generator := NewConversionGenerator(scheme, "foo") typedGenerator, ok := generator.(*conversionGenerator) if !ok { t.Fatalf("error converting to conversionGenerator") } internalType := reflect.TypeOf(Internal{}) externalType := reflect.TypeOf(External{}) err := typedGenerator.generateConversionsForStruct(internalType, externalType) if err == nil { t.Errorf("expected error for asymmetrical field") } // we are expecting Convert_runtime_InternalSubtype_To_runtime_ExternalSubtype to be generated // even though the conversion for the parent type cannot be auto generated if len(typedGenerator.publicFuncs) != 1 { t.Errorf("expected to find one public conversion for the Complex type but found: %v", typedGenerator.publicFuncs) } }
func TestExternalToInternalMapping(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) table := []struct { obj runtime.Object encoded string }{ { &InternalOptionalExtensionType{Extension: nil}, `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } for i, item := range table { gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a)) } } }
func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) { expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"} expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"} expectedGroupVersion3 := unversioned.GroupVersion{Group: "tgroup", Version: "test3"} internalObjectGK := unversioned.GroupKind{Group: "tgroup", Kind: "InternalObject"} otherObjectGK := unversioned.GroupKind{Group: "tgroup", Kind: "OtherObject"} mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, fakeInterfaces) mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace) mapper.Add(expectedGroupVersion2.WithKind("OtherObject"), RESTScopeNamespace) // pick default matching object kind based on search order mapping, err := mapper.RESTMapping(otherObjectGK) if err != nil { t.Fatalf("unexpected error: %v", err) } if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 { t.Errorf("unexpected mapping: %#v", mapping) } mapping, err = mapper.RESTMapping(internalObjectGK) if err != nil { t.Fatalf("unexpected error: %v", err) } if mapping.Resource != "internalobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion1 { t.Errorf("unexpected mapping: %#v", mapping) } // mismatch of version mapping, err = mapper.RESTMapping(internalObjectGK, expectedGroupVersion2.Version) if err == nil { t.Errorf("unexpected non-error") } mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion1.Version) if err == nil { t.Errorf("unexpected non-error") } // not in the search versions mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version) if err == nil { t.Errorf("unexpected non-error") } // explicit search order mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version, expectedGroupVersion1.Version) if err == nil { t.Errorf("unexpected non-error") } mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version, expectedGroupVersion2.Version) if err != nil { t.Fatalf("unexpected error: %v", err) } if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 { t.Errorf("unexpected mapping: %#v", mapping) } }
func TestMetaValues(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"} s := runtime.NewScheme() s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) internalToExternalCalls := 0 externalToInternalCalls := 0 // Register functions to verify that scope.Meta() gets set correctly. err := s.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { t.Logf("internal -> external") scope.Convert(&in.TestString, &out.TestString, 0) internalToExternalCalls++ return nil }, func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { t.Logf("external -> internal") scope.Convert(&in.TestString, &out.TestString, 0) externalToInternalCalls++ return nil }, ) if err != nil { t.Fatalf("unexpected error: %v", err) } simple := &InternalSimple{ TestString: "foo", } s.Log(t) out, err := s.ConvertToVersion(simple, externalGV) if err != nil { t.Fatalf("unexpected error: %v", err) } internal, err := s.ConvertToVersion(out, internalGV) if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := simple, internal; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } if e, a := 1, internalToExternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } if e, a := 1, externalToInternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } }
// RunExplain executes the appropriate steps to print a model's documentation func RunExplain(f *cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string) error { if len(args) == 0 { fmt.Fprint(cmdErr, "You must specify the type of resource to explain. ", valid_resources) return cmdutil.UsageError(cmd, "Required resource not specified.") } if len(args) > 1 { return cmdutil.UsageError(cmd, "We accept only this format: explain RESOURCE") } recursive := cmdutil.GetFlagBool(cmd, "recursive") apiVersionString := cmdutil.GetFlagString(cmd, "api-version") apiVersion := unversioned.GroupVersion{} mapper, _ := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) // TODO: After we figured out the new syntax to separate group and resource, allow // the users to use it in explain (kubectl explain <group><syntax><resource>). // Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax. inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper) if err != nil { return err } // TODO: We should deduce the group for a resource by discovering the supported resources at server. fullySpecifiedGVR, groupResource := unversioned.ParseResourceArg(inModel) gvk := unversioned.GroupVersionKind{} if fullySpecifiedGVR != nil { gvk, _ = mapper.KindFor(*fullySpecifiedGVR) } if gvk.Empty() { gvk, err = mapper.KindFor(groupResource.WithVersion("")) if err != nil { return err } } if len(apiVersionString) == 0 { groupMeta, err := registered.Group(gvk.Group) if err != nil { return err } apiVersion = groupMeta.GroupVersion } else { apiVersion, err = unversioned.ParseGroupVersion(apiVersionString) if err != nil { return nil } } schema, err := f.SwaggerSchema(apiVersion.WithKind(gvk.Kind)) if err != nil { return err } return kubectl.PrintModelDescription(inModel, fieldsPath, out, schema, recursive) }
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) { expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"} expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"} mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, unmatchedVersionInterfaces) mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace, false) _, err := mapper.RESTMapping("InternalObject", expectedGroupVersion1.String()) if err == nil { t.Errorf("unexpected non-error") } }
// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules. // Whenever an object of this type is serialized, it is serialized with the provided group version and is not // converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an // API group and version that would never be updated. // // TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into // every version with particular schemas. Resolve this method at that point. func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...Object) { s.AddKnownTypes(version, types...) for _, obj := range types { t := reflect.TypeOf(obj).Elem() gvk := version.WithKind(t.Name()) s.unversionedTypes[t] = gvk if _, ok := s.unversionedKinds[gvk.Kind]; ok { panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind)) } s.unversionedKinds[gvk.Kind] = t } }
// AddToGroupVersion registers the watch external and internal kinds with the scheme, and ensures the proper // conversions are in place. func AddToGroupVersion(scheme *runtime.Scheme, groupVersion unversioned.GroupVersion) { scheme.AddKnownTypeWithName(groupVersion.WithKind(WatchEventKind), &Event{}) scheme.AddKnownTypeWithName( unversioned.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}.WithKind(WatchEventKind), &InternalEvent{}, ) scheme.AddConversionFuncs( Convert_versioned_Event_to_watch_Event, Convert_versioned_InternalEvent_to_versioned_Event, Convert_watch_Event_to_versioned_Event, Convert_versioned_Event_to_versioned_InternalEvent, ) }
// ConvertToVersion attempts to convert an input object to its matching Kind in another // version within this scheme. Will return an error if the provided version does not // contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also // return an error if the conversion does not result in a valid Object being // returned. The serializer handles loading/serializing nested objects. func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) { switch in.(type) { case *Unknown, *Unstructured, *UnstructuredList: old := in.GetObjectKind().GroupVersionKind() defer in.GetObjectKind().SetGroupVersionKind(old) setTargetVersion(in, s, outVersion) return in, nil } t := reflect.TypeOf(in) if t.Kind() != reflect.Ptr { return nil, fmt.Errorf("only pointer types may be converted: %v", t) } t = t.Elem() if t.Kind() != reflect.Struct { return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t) } var kind unversioned.GroupVersionKind if unversionedKind, ok := s.unversionedTypes[t]; ok { kind = unversionedKind } else { kinds, ok := s.typeToGVK[t] if !ok || len(kinds) == 0 { return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion) } kind = kinds[0] } outKind := outVersion.WithKind(kind.Kind) inKinds, _, err := s.ObjectKinds(in) if err != nil { return nil, err } out, err := s.New(outKind) if err != nil { return nil, err } flags, meta := s.generateConvertMeta(inKinds[0].GroupVersion(), outVersion, in) if err := s.converter.Convert(in, out, flags, meta); err != nil { return nil, err } setTargetVersion(out, s, outVersion) return out, nil }
func TestUnversionedTypes(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} otherGV := unversioned.GroupVersion{Group: "group", Version: "other"} scheme := runtime.NewScheme() scheme.AddUnversionedTypes(externalGV, &InternalSimple{}) scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok { t.Fatalf("type not unversioned and in scheme: %t %t", unv, ok) } kinds, _, err := scheme.ObjectKinds(&InternalSimple{}) if err != nil { t.Fatal(err) } kind := kinds[0] if kind != externalGV.WithKind("InternalSimple") { t.Fatalf("unexpected: %#v", kind) } test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) if err != nil { t.Fatal(err) } obj2, gvk, err := codec.Decode(data, nil, nil) if err != nil { t.Fatal(err) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) } // object is serialized as an unversioned object (in the group and version it was defined in) if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) { t.Errorf("unexpected gvk returned by decode: %#v", gvk) } // when serialized to a different group, the object is kept in its preferred name codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV) data, err = runtime.Encode(codec, obj) if err != nil { t.Fatal(err) } if string(data) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" { t.Errorf("unexpected data: %s", data) } }
func TestInvalidObjectValueKind(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "", Version: ""} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) embedded := &runtime.EmbeddedObject{} switch obj := embedded.Object.(type) { default: _, err := scheme.ObjectKind(obj) if err == nil { t.Errorf("Expected error on invalid kind") } } }
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'. // Encode() will refuse objects unless their type has been registered with AddKnownTypes. // All objects passed to types should be pointers to structs. The name that go reports for // the struct becomes the "kind" field when encoding. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...interface{}) { for _, obj := range types { t := reflect.TypeOf(obj) if t.Kind() != reflect.Ptr { panic("All types must be pointers to structs.") } t = t.Elem() if t.Kind() != reflect.Struct { panic("All types must be pointers to structs.") } gvk := gv.WithKind(t.Name()) s.gvkToType[gvk] = t s.typeToGVK[t] = append(s.typeToGVK[t], gvk) } }
// NewUnstructuredObjectTyper returns a runtime.ObjectTyper for // unstructred objects based on discovery information. func NewUnstructuredObjectTyper(groupResources []*APIGroupResources) *UnstructuredObjectTyper { dot := &UnstructuredObjectTyper{registered: make(map[unversioned.GroupVersionKind]bool)} for _, group := range groupResources { for _, discoveryVersion := range group.Group.Versions { resources, ok := group.VersionedResources[discoveryVersion.Version] if !ok { continue } gv := unversioned.GroupVersion{Group: group.Group.Name, Version: discoveryVersion.Version} for _, resource := range resources { dot.registered[gv.WithKind(resource.Kind)] = true } } } return dot }
// RunExplain executes the appropriate steps to print a model's documentation func RunExplain(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { if len(args) != 1 { return cmdutil.UsageError(cmd, "We accept only this format: explain RESOURCE") } recursive := cmdutil.GetFlagBool(cmd, "recursive") apiVersionString := cmdutil.GetFlagString(cmd, "api-version") apiVersion := unversioned.GroupVersion{} mapper, _ := f.Object() // TODO: After we figured out the new syntax to separate group and resource, allow // the users to use it in explain (kubectl explain <group><syntax><resource>). // Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax. inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper) if err != nil { return err } // TODO: We should deduce the group for a resource by discovering the supported resources at server. gvk, err := mapper.KindFor(unversioned.ParseGroupResource(inModel).WithVersion("")) if err != nil { return err } if len(apiVersionString) == 0 { groupMeta, err := registered.Group(gvk.Group) if err != nil { return err } apiVersion = groupMeta.GroupVersion } else { apiVersion, err = unversioned.ParseGroupVersion(apiVersionString) if err != nil { return nil } } schema, err := f.SwaggerSchema(apiVersion.WithKind(gvk.Kind)) if err != nil { return err } return kubectl.PrintModelDescription(inModel, fieldsPath, out, schema, recursive) }
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'. // All objects passed to types should be pointers to structs. The name that go reports for // the struct becomes the "kind" field when encoding. Version may not be empty - use the // APIVersionInternal constant if you have a type that does not have a formal version. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { if len(gv.Version) == 0 { panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0])) } for _, obj := range types { t := reflect.TypeOf(obj) if t.Kind() != reflect.Ptr { panic("All types must be pointers to structs.") } t = t.Elem() if t.Kind() != reflect.Struct { panic("All types must be pointers to structs.") } gvk := gv.WithKind(t.Name()) s.gvkToType[gvk] = t s.typeToGVK[t] = append(s.typeToGVK[t], gvk) } }
func TestRESTMapper(t *testing.T) { gv := unversioned.GroupVersion{Group: "", Version: "v1"} rcGVK := gv.WithKind("ReplicationController") podTemplateGVK := gv.WithKind("PodTemplate") if gvk, err := latest.GroupOrDie("").RESTMapper.KindFor("replicationcontrollers"); err != nil || gvk != rcGVK { t.Errorf("unexpected version mapping: %v %v", gvk, err) } if m, err := latest.GroupOrDie("").RESTMapper.RESTMapping(podTemplateGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != podTemplateGVK || m.Resource != "podtemplates" { t.Errorf("unexpected version mapping: %#v %v", m, err) } for _, version := range latest.GroupOrDie("").Versions { currGroupVersion := unversioned.GroupVersion{Version: version} mapping, err := latest.GroupOrDie("").RESTMapper.RESTMapping(rcGVK.GroupKind(), currGroupVersion.String()) if err != nil { t.Errorf("unexpected error: %v", err) } if mapping.Resource != "replicationControllers" && mapping.Resource != "replicationcontrollers" { t.Errorf("incorrect resource name: %#v", mapping) } if mapping.GroupVersionKind.GroupVersion() != currGroupVersion { t.Errorf("incorrect version: %v", mapping) } interfaces, _ := latest.GroupOrDie("").InterfacesFor(currGroupVersion.String()) if mapping.Codec != interfaces.Codec { t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) } rc := &internal.ReplicationController{ObjectMeta: internal.ObjectMeta{Name: "foo"}} name, err := mapping.MetadataAccessor.Name(rc) if err != nil { t.Errorf("unexpected error: %v", err) } if name != "foo" { t.Errorf("unable to retrieve object meta with: %v", mapping.MetadataAccessor) } } }
// DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then // converts that object to into (if necessary). Returns an error if the operation cannot be completed. func (c *parameterCodec) DecodeParameters(parameters url.Values, from unversioned.GroupVersion, into Object) error { if len(parameters) == 0 { return nil } targetGVK, _, err := c.typer.ObjectKind(into) if err != nil { return err } if targetGVK.GroupVersion() == from { return c.convertor.Convert(¶meters, into) } input, err := c.creator.New(from.WithKind(targetGVK.Kind)) if err != nil { return err } if err := c.convertor.Convert(¶meters, input); err != nil { return err } return c.convertor.Convert(input, into) }
func TestRESTMapper(t *testing.T) { gv := unversioned.GroupVersion{Group: "extensions", Version: "v1beta1"} hpaGVK := gv.WithKind("HorizontalPodAutoscaler") daemonSetGVK := gv.WithKind("DaemonSet") if gvk, err := latest.GroupOrDie("extensions").RESTMapper.KindFor("horizontalpodautoscalers"); err != nil || gvk != hpaGVK { t.Errorf("unexpected version mapping: %v %v", gvk, err) } if m, err := latest.GroupOrDie("extensions").RESTMapper.RESTMapping(daemonSetGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != daemonSetGVK || m.Resource != "daemonsets" { t.Errorf("unexpected version mapping: %#v %v", m, err) } for _, version := range latest.GroupOrDie("extensions").Versions { mapping, err := latest.GroupOrDie("extensions").RESTMapper.RESTMapping(hpaGVK.GroupKind(), version) if err != nil { t.Errorf("unexpected error: %v", err) } if mapping.Resource != "horizontalpodautoscalers" { t.Errorf("incorrect resource name: %#v", mapping) } if mapping.GroupVersionKind.GroupVersion() != gv { t.Errorf("incorrect groupVersion: %v", mapping) } interfaces, _ := latest.GroupOrDie("extensions").InterfacesFor(gv.String()) if mapping.Codec != interfaces.Codec { t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) } rc := &extensions.HorizontalPodAutoscaler{ObjectMeta: api.ObjectMeta{Name: "foo"}} name, err := mapping.MetadataAccessor.Name(rc) if err != nil { t.Errorf("unexpected error: %v", err) } if name != "foo" { t.Errorf("unable to retrieve object meta with: %v", mapping.MetadataAccessor) } } }
// TestDeepCopyOfEmbeddedObject checks to make sure that EmbeddedObject's can be passed through DeepCopy with fidelity func TestDeepCopyOfEmbeddedObject(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") s := runtime.NewScheme() s.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) original := &EmbeddedTest{ ID: "outer", Object: runtime.EmbeddedObject{ Object: &EmbeddedTest{ ID: "inner", }, }, } originalData, err := s.EncodeToVersion(original, externalGV.String()) if err != nil { t.Errorf("unexpected error: %v", err) } t.Logf("originalRole = %v\n", string(originalData)) copyOfOriginal, err := api.Scheme.DeepCopy(original) if err != nil { t.Fatalf("unexpected error: %v", err) } copiedData, err := s.EncodeToVersion(copyOfOriginal.(runtime.Object), externalGV.String()) if err != nil { t.Errorf("unexpected error: %v", err) } t.Logf("copyOfRole = %v\n", string(copiedData)) if !reflect.DeepEqual(original, copyOfOriginal) { t.Errorf("expected \n%v\n, got \n%v", string(originalData), string(copiedData)) } }
// TestDeepCopyOfRuntimeObject checks to make sure that runtime.Objects's can be passed through DeepCopy with fidelity func TestDeepCopyOfRuntimeObject(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") s := runtime.NewScheme() s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) original := &EmbeddedTest{ ID: "outer", Object: &EmbeddedTest{ ID: "inner", }, } codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) originalData, err := runtime.Encode(codec, original) if err != nil { t.Errorf("unexpected error: %v", err) } t.Logf("originalRole = %v\n", string(originalData)) copyOfOriginal, err := s.DeepCopy(original) if err != nil { t.Fatalf("unexpected error: %v", err) } copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object)) if err != nil { t.Errorf("unexpected error: %v", err) } t.Logf("copyOfRole = %v\n", string(copiedData)) if !reflect.DeepEqual(original, copyOfOriginal) { t.Errorf("expected \n%v\n, got \n%v", string(originalData), string(copiedData)) } }