diff --git a/pkg/api/meta/restmapper_test.go b/pkg/api/meta/restmapper_test.go index 450d3cc20f..31df35b76b 100644 --- a/pkg/api/meta/restmapper_test.go +++ b/pkg/api/meta/restmapper_test.go @@ -28,6 +28,8 @@ import ( type fakeCodec struct{} +var _ runtime.Decoder = fakeCodec{} + func (fakeCodec) Encode(runtime.Object) ([]byte, error) { return []byte{}, nil } @@ -40,7 +42,7 @@ func (fakeCodec) Decode([]byte) (runtime.Object, error) { return nil, nil } -func (fakeCodec) DecodeToVersion([]byte, string) (runtime.Object, error) { +func (fakeCodec) DecodeToVersion([]byte, unversioned.GroupVersion) (runtime.Object, error) { return nil, nil } @@ -48,7 +50,7 @@ func (fakeCodec) DecodeInto([]byte, runtime.Object) error { return nil } -func (fakeCodec) DecodeIntoWithSpecifiedVersionKind([]byte, runtime.Object, string, string) error { +func (fakeCodec) DecodeIntoWithSpecifiedVersionKind([]byte, runtime.Object, unversioned.GroupVersionKind) error { return nil } diff --git a/pkg/api/unversioned/group_version.go b/pkg/api/unversioned/group_version.go index 82826b4ba2..18f1ba63bf 100644 --- a/pkg/api/unversioned/group_version.go +++ b/pkg/api/unversioned/group_version.go @@ -52,6 +52,11 @@ type GroupVersion struct { // String puts "group" and "version" into a single "group/version" string. For the legacy v1 // it returns "v1". func (gv GroupVersion) String() string { + // special case the internal apiVersion for kube + if len(gv.Group) == 0 && len(gv.Version) == 0 { + return "" + } + // special case of "v1" for backward compatibility if gv.Group == "" && gv.Version == "v1" { return gv.Version @@ -63,6 +68,11 @@ func (gv GroupVersion) String() string { // ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error // if it cannot parse the string. func ParseGroupVersion(gv string) (GroupVersion, error) { + // this can be the internal version for kube + if (len(gv) == 0) || (gv == "/") { + return GroupVersion{}, nil + } + s := strings.Split(gv, "/") // "v1" is the only special case. Otherwise GroupVersion is expected to contain // one "/" dividing the string into two parts. diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 334519661a..1246bc22a1 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -343,7 +343,8 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object } obj := r.New() - if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.APIVersion, scope.Kind); err != nil { + // TODO this cleans up with proper typing + if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, unversioned.ParseGroupVersionOrDie(scope.APIVersion).WithKind(scope.Kind)); err != nil { err = transformDecodeError(typer, err, obj, body) errorJSON(err, scope.Codec, w) return @@ -576,7 +577,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType } obj := r.New() - if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.APIVersion, scope.Kind); err != nil { + if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, unversioned.ParseGroupVersionOrDie(scope.APIVersion).WithKind(scope.Kind)); err != nil { err = transformDecodeError(typer, err, obj, body) errorJSON(err, scope.Codec, w) return diff --git a/pkg/conversion/decode.go b/pkg/conversion/decode.go index d799cfffb8..8d9801559d 100644 --- a/pkg/conversion/decode.go +++ b/pkg/conversion/decode.go @@ -22,6 +22,8 @@ import ( "net/url" "github.com/ugorji/go/codec" + + "k8s.io/kubernetes/pkg/api/unversioned" ) func (s *Scheme) DecodeToVersionedObject(data []byte) (obj interface{}, version, kind string, err error) { @@ -52,7 +54,8 @@ func (s *Scheme) DecodeToVersionedObject(data []byte) (obj interface{}, version, // s.InternalVersion type before being returned. Decode will not decode // objects without version set unless InternalVersion is also "". func (s *Scheme) Decode(data []byte) (interface{}, error) { - return s.DecodeToVersion(data, s.InternalVersion) + // TODO this is cleaned up when internal types are fixed + return s.DecodeToVersion(data, unversioned.ParseGroupVersionOrDie(s.InternalVersion)) } // DecodeToVersion converts a JSON string back into a pointer to an api object. @@ -60,7 +63,7 @@ func (s *Scheme) Decode(data []byte) (interface{}, error) { // 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 "". -func (s *Scheme) DecodeToVersion(data []byte, version string) (interface{}, error) { +func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (interface{}, error) { obj, sourceVersion, kind, err := s.DecodeToVersionedObject(data) if err != nil { return nil, err @@ -71,12 +74,12 @@ func (s *Scheme) DecodeToVersion(data []byte, version string) (interface{}, erro } // Convert if needed. - if version != sourceVersion { - objOut, err := s.NewObject(version, kind) + if gv.String() != sourceVersion { + objOut, err := s.NewObject(gv.String(), kind) if err != nil { return nil, err } - flags, meta := s.generateConvertMeta(sourceVersion, version, obj) + flags, meta := s.generateConvertMeta(sourceVersion, gv.String(), obj) if err := s.converter.Convert(obj, objOut, flags, meta); err != nil { return nil, err } @@ -91,7 +94,7 @@ func (s *Scheme) DecodeToVersion(data []byte, version string) (interface{}, erro // If obj's version doesn't match that in data, an attempt will be made to convert // data into obj's version. func (s *Scheme) DecodeInto(data []byte, obj interface{}) error { - return s.DecodeIntoWithSpecifiedVersionKind(data, obj, "", "") + return s.DecodeIntoWithSpecifiedVersionKind(data, obj, unversioned.GroupVersionKind{}) } // DecodeIntoWithSpecifiedVersionKind compares the passed in specifiedVersion and @@ -101,7 +104,7 @@ func (s *Scheme) DecodeInto(data []byte, obj interface{}) error { // The function then implements the functionality of DecodeInto. // If specifiedVersion and specifiedKind are empty, the function degenerates to // DecodeInto. -func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}, specifiedVersion, specifiedKind string) error { +func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}, gvk unversioned.GroupVersionKind) error { if len(data) == 0 { return errors.New("empty input") } @@ -110,16 +113,16 @@ func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{} return err } if dataVersion == "" { - dataVersion = specifiedVersion + dataVersion = gvk.GroupVersion().String() } if dataKind == "" { - dataKind = specifiedKind + dataKind = gvk.Kind } - if len(specifiedVersion) > 0 && (dataVersion != specifiedVersion) { - return errors.New(fmt.Sprintf("The apiVersion in the data (%s) does not match the specified apiVersion(%s)", dataVersion, specifiedVersion)) + if (len(gvk.GroupVersion().Group) > 0 || len(gvk.GroupVersion().Version) > 0) && (dataVersion != gvk.GroupVersion().String()) { + return errors.New(fmt.Sprintf("The apiVersion in the data (%s) does not match the specified apiVersion(%v)", dataVersion, gvk.GroupVersion())) } - if len(specifiedKind) > 0 && (dataKind != specifiedKind) { - return errors.New(fmt.Sprintf("The kind in the data (%s) does not match the specified kind(%s)", dataKind, specifiedKind)) + if len(gvk.Kind) > 0 && (dataKind != gvk.Kind) { + return errors.New(fmt.Sprintf("The kind in the data (%s) does not match the specified kind(%v)", dataKind, gvk)) } objVersion, objKind, err := s.ObjectVersionAndKind(obj) diff --git a/pkg/conversion/scheme.go b/pkg/conversion/scheme.go index d0e2864613..2a3013474c 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -158,14 +158,14 @@ func (s *Scheme) KnownTypes(version string) map[string]reflect.Type { // NewObject returns a new object of the given version and name, // or an error if it hasn't been registered. -func (s *Scheme) NewObject(versionName, kind string) (interface{}, error) { - if types, ok := s.versionMap[versionName]; ok { +func (s *Scheme) NewObject(gvString, kind string) (interface{}, error) { + if types, ok := s.versionMap[gvString]; ok { if t, ok := types[kind]; ok { return reflect.New(t).Interface(), nil } - return nil, ¬RegisteredErr{kind: kind, version: versionName} + return nil, ¬RegisteredErr{kind: kind, version: gvString} } - return nil, ¬RegisteredErr{kind: kind, version: versionName} + return nil, ¬RegisteredErr{kind: kind, version: gvString} } // AddConversionFuncs adds functions to the list of conversion functions. The given diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 42ac2cdf2a..1ff90e3b62 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -217,7 +217,7 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { if err != nil { return err } - decodedObj, err := scheme.DecodeToVersion(rawObj, "") + decodedObj, err := scheme.DecodeToVersion(rawObj, unversioned.GroupVersion{}) if err != nil { return err } diff --git a/pkg/registry/thirdpartyresourcedata/codec.go b/pkg/registry/thirdpartyresourcedata/codec.go index 4b31cc6bba..a9de614aae 100644 --- a/pkg/registry/thirdpartyresourcedata/codec.go +++ b/pkg/registry/thirdpartyresourcedata/codec.go @@ -154,7 +154,7 @@ func (t *thirdPartyResourceDataCodec) Decode(data []byte) (runtime.Object, error return result, nil } -func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, version string) (runtime.Object, error) { +func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (runtime.Object, error) { // TODO: this is hacky, there must be a better way... obj, err := t.Decode(data) if err != nil { @@ -164,7 +164,7 @@ func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, version strin if err != nil { return nil, err } - return t.delegate.DecodeToVersion(objData, version) + return t.delegate.DecodeToVersion(objData, gv) } func (t *thirdPartyResourceDataCodec) DecodeInto(data []byte, obj runtime.Object) error { @@ -175,14 +175,14 @@ func (t *thirdPartyResourceDataCodec) DecodeInto(data []byte, obj runtime.Object return t.populate(thirdParty, data) } -func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, version, kind string) error { +func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, gvk unversioned.GroupVersionKind) error { thirdParty, ok := obj.(*extensions.ThirdPartyResourceData) if !ok { return fmt.Errorf("unexpected object: %#v", obj) } - if kind != "ThirdPartyResourceData" { - return fmt.Errorf("unexpeceted kind: %s", kind) + if gvk.Kind != "ThirdPartyResourceData" { + return fmt.Errorf("unexpeceted kind: %s", gvk.Kind) } var dataObj interface{} @@ -194,25 +194,25 @@ func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data [] return fmt.Errorf("unexpcted object: %#v", dataObj) } if kindObj, found := mapObj["kind"]; !found { - mapObj["kind"] = kind + mapObj["kind"] = gvk.Kind } else { kindStr, ok := kindObj.(string) if !ok { return fmt.Errorf("unexpected object for 'kind': %v", kindObj) } if kindStr != t.kind { - return fmt.Errorf("kind doesn't match, expecting: %s, got %s", kind, kindStr) + return fmt.Errorf("kind doesn't match, expecting: %s, got %s", gvk.Kind, kindStr) } } if versionObj, found := mapObj["apiVersion"]; !found { - mapObj["apiVersion"] = version + mapObj["apiVersion"] = gvk.GroupVersion().String() } else { versionStr, ok := versionObj.(string) if !ok { return fmt.Errorf("unexpected object for 'apiVersion': %v", versionObj) } - if versionStr != version { - return fmt.Errorf("version doesn't match, expecting: %s, got %s", version, versionStr) + if versionStr != gvk.GroupVersion().String() { + return fmt.Errorf("version doesn't match, expecting: %v, got %s", gvk.GroupVersion(), versionStr) } } diff --git a/pkg/runtime/codec.go b/pkg/runtime/codec.go index 5d06482ea5..d69269e479 100644 --- a/pkg/runtime/codec.go +++ b/pkg/runtime/codec.go @@ -35,6 +35,7 @@ type yamlCodec struct { // yamlCodec implements Codec var _ Codec = yamlCodec{} +var _ Decoder = yamlCodec{} // YAMLDecoder adds YAML decoding support to a codec that supports JSON. func YAMLDecoder(codec Codec) Codec { @@ -75,6 +76,8 @@ type codecWrapper struct { version string } +var _ Decoder = &codecWrapper{} + // Encode implements Codec func (c *codecWrapper) Encode(obj Object) ([]byte, error) { return c.EncodeToVersion(obj, c.version) diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go index c367a4f364..b050a9ad02 100644 --- a/pkg/runtime/interfaces.go +++ b/pkg/runtime/interfaces.go @@ -19,6 +19,8 @@ package runtime import ( "io" "net/url" + + "k8s.io/kubernetes/pkg/api/unversioned" ) // Codec defines methods for serializing and deserializing API objects. @@ -31,10 +33,10 @@ type Codec interface { type Decoder interface { Decode(data []byte) (Object, error) // TODO: Remove this method? - DecodeToVersion(data []byte, version string) (Object, error) + DecodeToVersion(data []byte, groupVersion unversioned.GroupVersion) (Object, error) DecodeInto(data []byte, obj Object) error // TODO: Remove this method? - DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, kind, version string) error + DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, groupVersionKind unversioned.GroupVersionKind) error DecodeParametersInto(parameters url.Values, obj Object) error } diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 147c1a26fc..e9268802db 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -23,6 +23,7 @@ import ( "net/url" "reflect" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" ) @@ -35,6 +36,8 @@ type Scheme struct { fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc } +var _ Decoder = &Scheme{} + // Function to convert a field selector to internal representation. type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error) @@ -456,8 +459,8 @@ func (s *Scheme) Decode(data []byte) (Object, error) { // are set by Encode. Only versioned objects (APIVersion != "") are // accepted. The object will be converted into the in-memory versioned type // requested before being returned. -func (s *Scheme) DecodeToVersion(data []byte, version string) (Object, error) { - obj, err := s.raw.DecodeToVersion(data, version) +func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) { + obj, err := s.raw.DecodeToVersion(data, gv) if err != nil { return nil, err } @@ -476,8 +479,8 @@ func (s *Scheme) DecodeInto(data []byte, obj Object) error { return s.raw.DecodeInto(data, obj) } -func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, version, kind string) error { - return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, version, kind) +func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error { + return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, gvk) } func (s *Scheme) DecodeParametersInto(parameters url.Values, obj Object) error { diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index 83f8d4c6e2..401c3cbd30 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -20,6 +20,7 @@ import ( "reflect" "testing" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" ) @@ -43,9 +44,12 @@ func (*InternalSimple) IsAnAPIObject() {} func (*ExternalSimple) IsAnAPIObject() {} func TestScheme(t *testing.T) { + internalGVK := unversioned.GroupVersionKind{Group: "test.group", Version: "", Kind: "Simple"} + externalGVK := unversioned.GroupVersionKind{Group: "test.group", Version: "externalVersion", Kind: "Simple"} + scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName("", "Simple", &InternalSimple{}) - scheme.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{}) + scheme.AddKnownTypeWithName(internalGVK.GroupVersion().String(), internalGVK.Kind, &InternalSimple{}) + scheme.AddKnownTypeWithName(externalGVK.GroupVersion().String(), externalGVK.Kind, &ExternalSimple{}) // test that scheme is an ObjectTyper var _ runtime.ObjectTyper = scheme @@ -56,10 +60,10 @@ func TestScheme(t *testing.T) { // Register functions to verify that scope.Meta() gets set correctly. err := scheme.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { - if e, a := "", scope.Meta().SrcVersion; e != a { + if e, a := internalGVK.GroupVersion().String(), scope.Meta().SrcVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } - if e, a := "externalVersion", scope.Meta().DestVersion; e != a { + if e, a := externalGVK.GroupVersion().String(), scope.Meta().DestVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) @@ -68,10 +72,10 @@ func TestScheme(t *testing.T) { return nil }, func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { - if e, a := "externalVersion", scope.Meta().SrcVersion; e != a { + if e, a := externalGVK.GroupVersion().String(), scope.Meta().SrcVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } - if e, a := "", scope.Meta().DestVersion; e != a { + if e, a := internalGVK.GroupVersion().String(), scope.Meta().DestVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) @@ -93,7 +97,7 @@ func TestScheme(t *testing.T) { obj2, err2 := scheme.Decode(data) obj3 := &InternalSimple{} err3 := scheme.DecodeInto(data, obj3) - obj4, err4 := scheme.DecodeToVersion(data, "externalVersion") + obj4, err4 := scheme.DecodeToVersion(data, externalGVK.GroupVersion()) if err != nil || err2 != nil || err3 != nil || err4 != nil { t.Fatalf("Failure: '%v' '%v' '%v' '%v'", err, err2, err3, err4) } diff --git a/pkg/runtime/unstructured.go b/pkg/runtime/unstructured.go index 3c52423605..92506d4c1d 100644 --- a/pkg/runtime/unstructured.go +++ b/pkg/runtime/unstructured.go @@ -22,6 +22,7 @@ import ( "net/url" "reflect" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" ) @@ -31,6 +32,8 @@ var UnstructuredJSONScheme ObjectDecoder = unstructuredJSONScheme{} type unstructuredJSONScheme struct{} +var _ Decoder = unstructuredJSONScheme{} + // Recognizes returns true for any version or kind that is specified (internal // versions are specifically excluded). func (unstructuredJSONScheme) Recognizes(version, kind string) bool { @@ -75,11 +78,11 @@ func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error { return nil } -func (unstructuredJSONScheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, kind, version string) error { +func (unstructuredJSONScheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error { return nil } -func (unstructuredJSONScheme) DecodeToVersion(data []byte, version string) (Object, error) { +func (unstructuredJSONScheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) { return nil, nil }