use groupversion in RESTMapping

pull/6/head
deads2k 2015-11-13 08:13:55 -05:00
parent b675a77213
commit 303bcad398
19 changed files with 384 additions and 257 deletions

View File

@ -78,12 +78,16 @@ func TestRESTMapper(t *testing.T) {
t.Errorf("unexpected version mapping: %s %s %v", v, k, err)
}
if m, err := latest.GroupOrDie("").RESTMapper.RESTMapping("PodTemplate", ""); err != nil || m.APIVersion != "v1" || m.Resource != "podtemplates" {
expectedGroupVersion := unversioned.GroupVersion{Version: "v1"}
if m, err := latest.GroupOrDie("").RESTMapper.RESTMapping("PodTemplate", ""); err != nil || m.GroupVersionKind.GroupVersion() != expectedGroupVersion || m.Resource != "podtemplates" {
t.Errorf("unexpected version mapping: %#v %v", m, err)
}
for _, version := range latest.GroupOrDie("").Versions {
mapping, err := latest.GroupOrDie("").RESTMapper.RESTMapping("ReplicationController", version)
currGroupVersion := unversioned.GroupVersion{Version: version}
mapping, err := latest.GroupOrDie("").RESTMapper.RESTMapping("ReplicationController", currGroupVersion.String())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -91,11 +95,11 @@ func TestRESTMapper(t *testing.T) {
if mapping.Resource != "replicationControllers" && mapping.Resource != "replicationcontrollers" {
t.Errorf("incorrect resource name: %#v", mapping)
}
if mapping.APIVersion != version {
if mapping.GroupVersionKind.GroupVersion() != currGroupVersion {
t.Errorf("incorrect version: %v", mapping)
}
interfaces, _ := latest.GroupOrDie("").InterfacesFor(version)
interfaces, _ := latest.GroupOrDie("").InterfacesFor(currGroupVersion.String())
if mapping.Codec != interfaces.Codec {
t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces)
}

View File

@ -17,9 +17,11 @@ limitations under the License.
package api
import (
"fmt"
"strings"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util/sets"
)
@ -33,14 +35,23 @@ func RegisterRESTMapper(m meta.RESTMapper) {
RESTMapper = append(RESTMapper.(meta.MultiRESTMapper), m)
}
func NewDefaultRESTMapper(group string, versions []string, interfacesFunc meta.VersionInterfacesFunc,
func NewDefaultRESTMapper(group string, groupVersionStrings []string, interfacesFunc meta.VersionInterfacesFunc,
importPathPrefix string, ignoredKinds, rootScoped sets.String) *meta.DefaultRESTMapper {
mapper := meta.NewDefaultRESTMapper(group, versions, interfacesFunc)
mapper := meta.NewDefaultRESTMapper(group, groupVersionStrings, interfacesFunc)
// enumerate all supported versions, get the kinds, and register with the mapper how to address
// our resources.
for _, version := range versions {
for kind, oType := range Scheme.KnownTypes(version) {
for _, gvString := range groupVersionStrings {
gv, err := unversioned.ParseGroupVersion(gvString)
// TODO stop panicing when the types are fixed
if err != nil {
panic(err)
}
if gv.Group != group {
panic(fmt.Sprintf("%q does not match the expect %q", gv.Group, group))
}
for kind, oType := range Scheme.KnownTypes(gv.String()) {
// TODO: Remove import path prefix check.
// We check the import path prefix because we currently stuff both "api" and "extensions" objects
// into the same group within Scheme since Scheme has no notion of groups yet.
@ -51,7 +62,7 @@ func NewDefaultRESTMapper(group string, versions []string, interfacesFunc meta.V
if rootScoped.Has(kind) {
scope = meta.RESTScopeRoot
}
mapper.Add(scope, kind, version, false)
mapper.Add(scope, kind, gv.String(), false)
}
}
return mapper

View File

@ -17,6 +17,7 @@ limitations under the License.
package meta
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/types"
)
@ -124,10 +125,8 @@ type RESTScope interface {
type RESTMapping struct {
// Resource is a string representing the name of this resource as a REST client would see it
Resource string
// APIVersion represents the APIVersion that represents the resource as presented. It is provided
// for convenience for passing around a consistent mapping.
APIVersion string
Kind string
GroupVersionKind unversioned.GroupVersionKind
// Scope contains the information needed to deal with REST Resources that are in a resource hierarchy
Scope RESTScope

View File

@ -20,6 +20,8 @@ package meta
import (
"fmt"
"strings"
"k8s.io/kubernetes/pkg/api/unversioned"
)
// Implements RESTScope interface
@ -72,12 +74,12 @@ type typeMeta struct {
//
// TODO: Only accept plural for some operations for increased control?
// (`get pod bar` vs `get pods bar`)
// TODO these maps should be keyed based on GroupVersionKinds
type DefaultRESTMapper struct {
mapping map[string]typeMeta
reverse map[typeMeta]string
scopes map[typeMeta]RESTScope
group string
versions []string
groupVersions []unversioned.GroupVersion
plurals map[string]string
singulars map[string]string
interfacesFunc VersionInterfacesFunc
@ -92,7 +94,12 @@ type VersionInterfacesFunc func(apiVersion string) (*VersionInterfaces, error)
// and the Kubernetes API conventions. Takes a group name, a priority list of the versions
// to search when an object has no default version (set empty to return an error),
// and a function that retrieves the correct codec and metadata for a given version.
func NewDefaultRESTMapper(group string, versions []string, f VersionInterfacesFunc) *DefaultRESTMapper {
// TODO remove group when this API is fixed. It is no longer used.
// The external API for a RESTMapper is cross-version and this is currently called using
// group/version tuples. In the end, the structure may be easier to understand with
// a GroupRESTMapper and CrossGroupRESTMapper, but for now, this one is constructed and
// used a CrossGroupRESTMapper.
func NewDefaultRESTMapper(group string, gvStrings []string, f VersionInterfacesFunc) *DefaultRESTMapper {
mapping := make(map[string]typeMeta)
reverse := make(map[typeMeta]string)
scopes := make(map[typeMeta]RESTScope)
@ -100,23 +107,29 @@ func NewDefaultRESTMapper(group string, versions []string, f VersionInterfacesFu
singulars := make(map[string]string)
// TODO: verify name mappings work correctly when versions differ
gvs := []unversioned.GroupVersion{}
for _, gvString := range gvStrings {
gvs = append(gvs, unversioned.ParseGroupVersionOrDie(gvString))
}
return &DefaultRESTMapper{
mapping: mapping,
reverse: reverse,
scopes: scopes,
group: group,
versions: versions,
groupVersions: gvs,
plurals: plurals,
singulars: singulars,
interfacesFunc: f,
}
}
func (m *DefaultRESTMapper) Add(scope RESTScope, kind string, version string, mixedCase bool) {
func (m *DefaultRESTMapper) Add(scope RESTScope, kind string, gvString string, mixedCase bool) {
gv := unversioned.ParseGroupVersionOrDie(gvString)
plural, singular := KindToResource(kind, mixedCase)
m.plurals[singular] = plural
m.singulars[plural] = singular
meta := typeMeta{APIVersion: version, Kind: kind}
meta := typeMeta{APIVersion: gv.String(), Kind: kind}
_, ok1 := m.mapping[plural]
_, ok2 := m.mapping[strings.ToLower(plural)]
if !ok1 && !ok2 {
@ -177,74 +190,90 @@ func (m *DefaultRESTMapper) VersionAndKindForResource(resource string) (defaultV
}
func (m *DefaultRESTMapper) GroupForResource(resource string) (string, error) {
if _, ok := m.mapping[strings.ToLower(resource)]; !ok {
typemeta, exists := m.mapping[strings.ToLower(resource)]
if !exists {
return "", fmt.Errorf("in group for resource, no resource %q has been defined", resource)
}
return m.group, nil
gv, err := unversioned.ParseGroupVersion(typemeta.APIVersion)
if err != nil {
return "", err
}
return gv.Group, nil
}
// RESTMapping returns a struct representing the resource path and conversion interfaces a
// RESTClient should use to operate on the provided kind in order of versions. If a version search
// order is not provided, the search order provided to DefaultRESTMapper will be used to resolve which
// APIVersion should be used to access the named kind.
// TODO version here in this RESTMapper means just APIVersion, but the RESTMapper API is intended to handle multiple groups
// So this API is broken. The RESTMapper test made it clear that versions here were API versions, but the code tries to use
// them with group/version tuples.
// TODO this should probably become RESTMapping(GroupKind, versions ...string)
func (m *DefaultRESTMapper) RESTMapping(kind string, versions ...string) (*RESTMapping, error) {
// Pick an appropriate version
var version string
var groupVersion *unversioned.GroupVersion
hadVersion := false
for _, v := range versions {
if len(v) == 0 {
continue
}
currGroupVersion, err := unversioned.ParseGroupVersion(v)
if err != nil {
return nil, err
}
hadVersion = true
if _, ok := m.reverse[typeMeta{APIVersion: v, Kind: kind}]; ok {
version = v
if _, ok := m.reverse[typeMeta{APIVersion: currGroupVersion.String(), Kind: kind}]; ok {
groupVersion = &currGroupVersion
break
}
}
// Use the default preferred versions
if !hadVersion && len(version) == 0 {
for _, v := range m.versions {
if _, ok := m.reverse[typeMeta{APIVersion: v, Kind: kind}]; ok {
version = v
if !hadVersion && (groupVersion == nil) {
for _, currGroupVersion := range m.groupVersions {
if _, ok := m.reverse[typeMeta{APIVersion: currGroupVersion.String(), Kind: kind}]; ok {
groupVersion = &currGroupVersion
break
}
}
}
if len(version) == 0 {
if groupVersion == nil {
return nil, fmt.Errorf("no kind named %q is registered in versions %q", kind, versions)
}
gvk := unversioned.NewGroupVersionKind(*groupVersion, kind)
// Ensure we have a REST mapping
resource, ok := m.reverse[typeMeta{APIVersion: version, Kind: kind}]
resource, ok := m.reverse[typeMeta{APIVersion: gvk.GroupVersion().String(), Kind: gvk.Kind}]
if !ok {
found := []string{}
for _, v := range m.versions {
if _, ok := m.reverse[typeMeta{APIVersion: v, Kind: kind}]; ok {
found = append(found, v)
found := []unversioned.GroupVersion{}
for _, gv := range m.groupVersions {
if _, ok := m.reverse[typeMeta{APIVersion: gv.String(), Kind: kind}]; ok {
found = append(found, gv)
}
}
if len(found) > 0 {
return nil, fmt.Errorf("object with kind %q exists in versions %q, not %q", kind, strings.Join(found, ", "), version)
return nil, fmt.Errorf("object with kind %q exists in versions %v, not %v", kind, found, *groupVersion)
}
return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported object", version, kind)
return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported object", groupVersion, kind)
}
// Ensure we have a REST scope
scope, ok := m.scopes[typeMeta{APIVersion: version, Kind: kind}]
scope, ok := m.scopes[typeMeta{APIVersion: gvk.GroupVersion().String(), Kind: gvk.Kind}]
if !ok {
return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", version, kind)
return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", gvk.GroupVersion().String(), gvk.Kind)
}
interfaces, err := m.interfacesFunc(version)
interfaces, err := m.interfacesFunc(gvk.GroupVersion().String())
if err != nil {
return nil, fmt.Errorf("the provided version %q has no relevant versions", version)
return nil, fmt.Errorf("the provided version %q has no relevant versions", gvk.GroupVersion().String())
}
retVal := &RESTMapping{
Resource: resource,
APIVersion: version,
Kind: kind,
Scope: scope,
Resource: resource,
GroupVersionKind: gvk,
Scope: scope,
Codec: interfaces.Codec,
ObjectConvertor: interfaces.ObjectConvertor,

View File

@ -21,6 +21,7 @@ import (
"io"
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
)
@ -79,53 +80,71 @@ func unmatchedVersionInterfaces(version string) (*VersionInterfaces, error) {
}
func TestRESTMapperVersionAndKindForResource(t *testing.T) {
testGroup := "test.group"
testVersion := "test"
testGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: testVersion}
testCases := []struct {
Resource string
Kind, APIVersion string
MixedCase bool
Err bool
Resource string
GroupVersionToRegister unversioned.GroupVersion
ExpectedGVK unversioned.GroupVersionKind
MixedCase bool
Err bool
}{
{Resource: "internalobjec", Err: true},
{Resource: "internalObjec", Err: true},
{Resource: "internalobjec", Err: true, GroupVersionToRegister: testGroupVersion},
{Resource: "internalObjec", Err: true, GroupVersionToRegister: testGroupVersion},
{Resource: "internalobject", Kind: "InternalObject", APIVersion: "test"},
{Resource: "internalobjects", Kind: "InternalObject", APIVersion: "test"},
{Resource: "internalobject", GroupVersionToRegister: testGroupVersion, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")},
{Resource: "internalobjects", GroupVersionToRegister: testGroupVersion, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")},
{Resource: "internalobject", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
{Resource: "internalobjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
{Resource: "internalobject", GroupVersionToRegister: testGroupVersion, MixedCase: true, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")},
{Resource: "internalobjects", GroupVersionToRegister: testGroupVersion, MixedCase: true, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")},
{Resource: "internalObject", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
{Resource: "internalObjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
{Resource: "internalObject", GroupVersionToRegister: testGroupVersion, MixedCase: true, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")},
{Resource: "internalObjects", GroupVersionToRegister: testGroupVersion, MixedCase: true, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper("tgroup", []string{"test"}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
mapper := NewDefaultRESTMapper(testGroup, []string{testGroupVersion.String()}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, testCase.ExpectedGVK.Kind, testCase.GroupVersionToRegister.String(), testCase.MixedCase)
v, k, err := mapper.VersionAndKindForResource(testCase.Resource)
hasErr := err != nil
if hasErr != testCase.Err {
t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
continue
}
if v != testCase.APIVersion || k != testCase.Kind {
t.Errorf("%d: unexpected version and kind: %s %s", i, v, k)
if err != nil {
continue
}
actualGV, err := unversioned.ParseGroupVersion(v)
if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
continue
}
actualGVK := unversioned.NewGroupVersionKind(actualGV, k)
if actualGVK != testCase.ExpectedGVK {
t.Errorf("%d: unexpected version and kind: e=%s a=%s", i, testCase.ExpectedGVK, actualGVK)
}
}
}
func TestRESTMapperGroupForResource(t *testing.T) {
testCases := []struct {
Resource string
Kind, APIVersion, Group string
Err bool
Resource string
Kind string
GroupVersion unversioned.GroupVersion
Err bool
}{
{Resource: "myObject", Kind: "MyObject", APIVersion: "test", Group: "testapi"},
{Resource: "myobject", Kind: "MyObject", APIVersion: "test", Group: "testapi2"},
{Resource: "myObje", Err: true, Kind: "MyObject", APIVersion: "test", Group: "testapi"},
{Resource: "myobje", Err: true, Kind: "MyObject", APIVersion: "test", Group: "testapi"},
{Resource: "myObject", Kind: "MyObject", GroupVersion: unversioned.GroupVersion{Group: "testapi", Version: "test"}},
{Resource: "myobject", Kind: "MyObject", GroupVersion: unversioned.GroupVersion{Group: "testapi2", Version: "test"}},
{Resource: "myObje", Err: true, Kind: "MyObject", GroupVersion: unversioned.GroupVersion{Group: "testapi", Version: "test"}},
{Resource: "myobje", Err: true, Kind: "MyObject", GroupVersion: unversioned.GroupVersion{Group: "testapi", Version: "test"}},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper(testCase.Group, []string{"test"}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, false)
mapper := NewDefaultRESTMapper(testCase.GroupVersion.Group, []string{testCase.GroupVersion.String()}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.GroupVersion.String(), false)
g, err := mapper.GroupForResource(testCase.Resource)
if testCase.Err {
if err == nil {
@ -133,8 +152,8 @@ func TestRESTMapperGroupForResource(t *testing.T) {
}
} else if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
} else if g != testCase.Group {
t.Errorf("%d: expected group %q, got %q", i, testCase.Group, g)
} else if g != testCase.GroupVersion.Group {
t.Errorf("%d: expected group %q, got %q", i, testCase.GroupVersion.Group, g)
}
}
}
@ -170,31 +189,34 @@ func TestKindToResource(t *testing.T) {
func TestRESTMapperResourceSingularizer(t *testing.T) {
testCases := []struct {
Kind, APIVersion string
MixedCase bool
Plural string
Singular string
Kind string
MixedCase bool
Plural string
Singular string
}{
{Kind: "Pod", APIVersion: "test", MixedCase: true, Plural: "pods", Singular: "pod"},
{Kind: "Pod", APIVersion: "test", MixedCase: false, Plural: "pods", Singular: "pod"},
{Kind: "Pod", MixedCase: true, Plural: "pods", Singular: "pod"},
{Kind: "Pod", MixedCase: false, Plural: "pods", Singular: "pod"},
{Kind: "ReplicationController", APIVersion: "test", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"},
{Kind: "ReplicationController", APIVersion: "test", MixedCase: false, Plural: "replicationcontrollers", Singular: "replicationcontroller"},
{Kind: "ReplicationController", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"},
{Kind: "ReplicationController", MixedCase: false, Plural: "replicationcontrollers", Singular: "replicationcontroller"},
{Kind: "ImageRepository", APIVersion: "test", MixedCase: true, Plural: "imageRepositories", Singular: "imageRepository"},
{Kind: "ImageRepository", APIVersion: "test", MixedCase: false, Plural: "imagerepositories", Singular: "imagerepository"},
{Kind: "ImageRepository", MixedCase: true, Plural: "imageRepositories", Singular: "imageRepository"},
{Kind: "ImageRepository", MixedCase: false, Plural: "imagerepositories", Singular: "imagerepository"},
{Kind: "Status", APIVersion: "test", MixedCase: true, Plural: "statuses", Singular: "status"},
{Kind: "Status", APIVersion: "test", MixedCase: false, Plural: "statuses", Singular: "status"},
{Kind: "Status", MixedCase: true, Plural: "statuses", Singular: "status"},
{Kind: "Status", MixedCase: false, Plural: "statuses", Singular: "status"},
{Kind: "lowercase", APIVersion: "test", MixedCase: false, Plural: "lowercases", Singular: "lowercase"},
{Kind: "lowercase", MixedCase: false, Plural: "lowercases", Singular: "lowercase"},
// Don't add extra s if the original object is already plural
{Kind: "lowercases", APIVersion: "test", MixedCase: false, Plural: "lowercases", Singular: "lowercases"},
{Kind: "lowercases", MixedCase: false, Plural: "lowercases", Singular: "lowercases"},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper("tgroup", []string{"test"}, fakeInterfaces)
testGroupVersion := unversioned.GroupVersion{Group: "tgroup", Version: "test"}
mapper := NewDefaultRESTMapper(testGroupVersion.Group, []string{testGroupVersion.String()}, fakeInterfaces)
// create singular/plural mapping
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
mapper.Add(RESTScopeNamespace, testCase.Kind, testGroupVersion.String(), testCase.MixedCase)
singular, _ := mapper.ResourceSingularizer(testCase.Plural)
if singular != testCase.Singular {
t.Errorf("%d: mismatched singular: %s, should be %s", i, singular, testCase.Singular)
@ -203,37 +225,48 @@ func TestRESTMapperResourceSingularizer(t *testing.T) {
}
func TestRESTMapperRESTMapping(t *testing.T) {
testCases := []struct {
Kind string
APIVersions []string
MixedCase bool
DefaultVersions []string
testGroup := "tgroup"
testGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: "test"}
Resource string
Version string
Err bool
testCases := []struct {
Kind string
APIGroupVersions []unversioned.GroupVersion
MixedCase bool
DefaultVersions []string
Resource string
ExpectedGroupVersion *unversioned.GroupVersion
Err bool
}{
{Kind: "Unknown", Err: true},
{Kind: "InternalObject", Err: true},
{DefaultVersions: []string{"test"}, Kind: "Unknown", Err: true},
{DefaultVersions: []string{testGroupVersion.String()}, Kind: "Unknown", Err: true},
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
{DefaultVersions: []string{testGroupVersion.String()}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"},
{DefaultVersions: []string{testGroupVersion.String()}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"},
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
{DefaultVersions: []string{testGroupVersion.String()}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"},
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{}, Resource: "internalobjects", Version: "test"},
{DefaultVersions: []string{testGroupVersion.String()}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{}, Resource: "internalobjects", ExpectedGroupVersion: &unversioned.GroupVersion{Group: testGroup, Version: "test"}},
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, MixedCase: true, Resource: "internalObjects"},
{DefaultVersions: []string{testGroupVersion.String()}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"},
{DefaultVersions: []string{testGroupVersion.String()}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, MixedCase: true, Resource: "internalObjects"},
// TODO: add test for a resource that exists in one version but not another
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper("tgroup", testCase.DefaultVersions, fakeInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", "test", testCase.MixedCase)
mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
internalGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: "test"}
mapper := NewDefaultRESTMapper(testGroup, testCase.DefaultVersions, fakeInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", internalGroupVersion.String(), testCase.MixedCase)
deprecatedGroupVersionStrings := []string{}
for _, gv := range testCase.APIGroupVersions {
deprecatedGroupVersionStrings = append(deprecatedGroupVersionStrings, gv.String())
}
mapping, err := mapper.RESTMapping(testCase.Kind, deprecatedGroupVersionStrings...)
hasErr := err != nil
if hasErr != testCase.Err {
t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
@ -244,30 +277,37 @@ func TestRESTMapperRESTMapping(t *testing.T) {
if mapping.Resource != testCase.Resource {
t.Errorf("%d: unexpected resource: %#v", i, mapping)
}
version := testCase.Version
if version == "" {
version = testCase.APIVersions[0]
}
if mapping.APIVersion != version {
t.Errorf("%d: unexpected version: %#v", i, mapping)
}
if mapping.Codec == nil || mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil {
t.Errorf("%d: missing codec and accessor: %#v", i, mapping)
}
groupVersion := testCase.ExpectedGroupVersion
if groupVersion == nil {
groupVersion = &testCase.APIGroupVersions[0]
}
if mapping.GroupVersionKind.GroupVersion() != *groupVersion {
t.Errorf("%d: unexpected version: %#v", i, mapping)
}
}
}
func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
mapper := NewDefaultRESTMapper("tgroup", []string{"test1", "test2"}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
mapper.Add(RESTScopeNamespace, "OtherObject", "test2", false)
expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"}
expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"}
expectedGroupVersion3 := unversioned.GroupVersion{Group: "tgroup", Version: "test3"}
mapper := NewDefaultRESTMapper("tgroup", []string{expectedGroupVersion1.String(), expectedGroupVersion2.String()}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", expectedGroupVersion1.String(), false)
mapper.Add(RESTScopeNamespace, "OtherObject", expectedGroupVersion2.String(), false)
// pick default matching object kind based on search order
mapping, err := mapper.RESTMapping("OtherObject")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 {
t.Errorf("unexpected mapping: %#v", mapping)
}
@ -275,45 +315,48 @@ func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if mapping.Resource != "internalobjects" || mapping.APIVersion != "test1" {
if mapping.Resource != "internalobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion1 {
t.Errorf("unexpected mapping: %#v", mapping)
}
// mismatch of version
mapping, err = mapper.RESTMapping("InternalObject", "test2")
mapping, err = mapper.RESTMapping("InternalObject", expectedGroupVersion2.String())
if err == nil {
t.Errorf("unexpected non-error")
}
mapping, err = mapper.RESTMapping("OtherObject", "test1")
mapping, err = mapper.RESTMapping("OtherObject", expectedGroupVersion1.String())
if err == nil {
t.Errorf("unexpected non-error")
}
// not in the search versions
mapping, err = mapper.RESTMapping("OtherObject", "test3")
mapping, err = mapper.RESTMapping("OtherObject", expectedGroupVersion3.String())
if err == nil {
t.Errorf("unexpected non-error")
}
// explicit search order
mapping, err = mapper.RESTMapping("OtherObject", "test3", "test1")
mapping, err = mapper.RESTMapping("OtherObject", expectedGroupVersion3.String(), expectedGroupVersion1.String())
if err == nil {
t.Errorf("unexpected non-error")
}
mapping, err = mapper.RESTMapping("OtherObject", "test3", "test2")
mapping, err = mapper.RESTMapping("OtherObject", expectedGroupVersion3.String(), expectedGroupVersion2.String())
if err != nil {
t.Fatalf("unexpected non-error")
t.Fatalf("unexpected error: %v", err)
}
if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 {
t.Errorf("unexpected mapping: %#v", mapping)
}
}
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
mapper := NewDefaultRESTMapper("tgroup", []string{"test1", "test2"}, unmatchedVersionInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
_, err := mapper.RESTMapping("InternalObject", "test1")
expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"}
expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"}
mapper := NewDefaultRESTMapper("tgroup", []string{expectedGroupVersion1.String(), expectedGroupVersion2.String()}, unmatchedVersionInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", expectedGroupVersion1.String(), false)
_, err := mapper.RESTMapping("InternalObject", expectedGroupVersion1.String())
if err == nil {
t.Errorf("unexpected non-error")
}

View File

@ -22,7 +22,26 @@ import (
"strings"
)
// TODO: We need to remove the GroupVersion in types.go. We use the name GroupVersion here temporarily.
// GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion
// to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling
type GroupVersionKind struct {
Group string
Version string
Kind string
}
func NewGroupVersionKind(gv GroupVersion, kind string) GroupVersionKind {
return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
}
func (gvk GroupVersionKind) GroupVersion() GroupVersion {
return GroupVersion{Group: gvk.Group, Version: gvk.Version}
}
func (gvk *GroupVersionKind) String() string {
return gvk.Group + "/" + gvk.Version + ", Kind=" + gvk.Kind
}
// GroupVersion contains the "group" and the "version", which uniquely identifies the API.
type GroupVersion struct {
Group string
@ -31,7 +50,7 @@ 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 {
func (gv GroupVersion) String() string {
// special case of "v1" for backward compatibility
if gv.Group == "" && gv.Version == "v1" {
return gv.Version

View File

@ -21,6 +21,7 @@ import (
"testing"
"k8s.io/kubernetes/pkg/api/latest"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/componentconfig"
)
@ -53,28 +54,36 @@ func TestInterfacesFor(t *testing.T) {
}
func TestRESTMapper(t *testing.T) {
if v, k, err := latest.GroupOrDie("componentconfig").RESTMapper.VersionAndKindForResource("kubeproxyconfiguration"); err != nil || v != "componentconfig/v1alpha1" || k != "KubeProxyConfiguration" {
t.Errorf("unexpected version mapping: %s %s %v", v, k, err)
expectedGroupVersion := unversioned.GroupVersion{Group: "componentconfig", Version: "v1alpha1"}
if v, k, err := latest.GroupOrDie("componentconfig").RESTMapper.VersionAndKindForResource("kubeproxyconfiguration"); err != nil || v != expectedGroupVersion.String() || k != "KubeProxyConfiguration" {
t.Errorf("unexpected version mapping: %q %q %v", v, k, err)
}
if m, err := latest.GroupOrDie("componentconfig").RESTMapper.RESTMapping("KubeProxyConfiguration", ""); err != nil || m.APIVersion != "componentconfig/v1alpha1" || m.Resource != "kubeproxyconfigurations" {
if m, err := latest.GroupOrDie("componentconfig").RESTMapper.RESTMapping("KubeProxyConfiguration", ""); err != nil || m.GroupVersionKind.GroupVersion() != expectedGroupVersion || m.Resource != "kubeproxyconfigurations" {
t.Errorf("unexpected version mapping: %#v %v", m, err)
}
for _, groupVersion := range latest.GroupOrDie("componentconfig").GroupVersions {
mapping, err := latest.GroupOrDie("componentconfig").RESTMapper.RESTMapping("KubeProxyConfiguration", groupVersion)
for _, groupVersionString := range latest.GroupOrDie("componentconfig").GroupVersions {
gv, err := unversioned.ParseGroupVersion(groupVersionString)
if err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
mapping, err := latest.GroupOrDie("componentconfig").RESTMapper.RESTMapping("KubeProxyConfiguration", gv.String())
if err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if mapping.Resource != "kubeproxyconfigurations" {
t.Errorf("incorrect resource name: %#v", mapping)
}
if mapping.APIVersion != groupVersion {
if mapping.GroupVersionKind.GroupVersion() != gv {
t.Errorf("incorrect groupVersion: %v", mapping)
}
interfaces, _ := latest.GroupOrDie("componentconfig").InterfacesFor(groupVersion)
interfaces, _ := latest.GroupOrDie("componentconfig").InterfacesFor(gv.String())
if mapping.Codec != interfaces.Codec {
t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces)
}

View File

@ -75,16 +75,20 @@ func TestInterfacesFor(t *testing.T) {
}
func TestRESTMapper(t *testing.T) {
if v, k, err := latest.GroupOrDie("extensions").RESTMapper.VersionAndKindForResource("horizontalpodautoscalers"); err != nil || v != "extensions/v1beta1" || k != "HorizontalPodAutoscaler" {
expectedGroupVersion := unversioned.GroupVersion{Group: "extensions", Version: "v1beta1"}
if v, k, err := latest.GroupOrDie("extensions").RESTMapper.VersionAndKindForResource("horizontalpodautoscalers"); err != nil || v != expectedGroupVersion.String() || k != "HorizontalPodAutoscaler" {
t.Errorf("unexpected version mapping: %s %s %v", v, k, err)
}
if m, err := latest.GroupOrDie("extensions").RESTMapper.RESTMapping("DaemonSet", ""); err != nil || m.APIVersion != "extensions/v1beta1" || m.Resource != "daemonsets" {
if m, err := latest.GroupOrDie("extensions").RESTMapper.RESTMapping("DaemonSet", ""); err != nil || m.GroupVersionKind.GroupVersion() != expectedGroupVersion || m.Resource != "daemonsets" {
t.Errorf("unexpected version mapping: %#v %v", m, err)
}
for _, groupVersion := range latest.GroupOrDie("extensions").GroupVersions {
mapping, err := latest.GroupOrDie("extensions").RESTMapper.RESTMapping("HorizontalPodAutoscaler", groupVersion)
for _, groupVersionString := range latest.GroupOrDie("extensions").GroupVersions {
gv, err := unversioned.ParseGroupVersion(groupVersionString)
mapping, err := latest.GroupOrDie("extensions").RESTMapper.RESTMapping("HorizontalPodAutoscaler", gv.String())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -92,11 +96,11 @@ func TestRESTMapper(t *testing.T) {
if mapping.Resource != "horizontalpodautoscalers" {
t.Errorf("incorrect resource name: %#v", mapping)
}
if mapping.APIVersion != groupVersion {
if mapping.GroupVersionKind.GroupVersion() != gv {
t.Errorf("incorrect groupVersion: %v", mapping)
}
interfaces, _ := latest.GroupOrDie("extensions").InterfacesFor(groupVersion)
interfaces, _ := latest.GroupOrDie("extensions").InterfacesFor(gv.String())
if mapping.Codec != interfaces.Codec {
t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces)
}

View File

@ -59,19 +59,16 @@ func convert(obj runtime.Object) (runtime.Object, error) {
// This creates fake API versions, similar to api/latest.go.
var testAPIGroup = "test.group"
var testGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version"}
var testVersion = testGroupVersion.String()
var newGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version2"}
var newVersion = newGroupVersion.String()
var prefix = "apis"
var grouplessGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"}
var grouplessVersion = grouplessGroupVersion.String()
var grouplessPrefix = "api"
var grouplessCodec = runtime.CodecFor(api.Scheme, grouplessVersion)
var grouplessCodec = runtime.CodecFor(api.Scheme, grouplessGroupVersion.String())
var versions = []string{grouplessVersion, testVersion, newVersion}
var codec = runtime.CodecFor(api.Scheme, testVersion)
var newCodec = runtime.CodecFor(api.Scheme, newVersion)
var groupVersions = []unversioned.GroupVersion{grouplessGroupVersion, testGroupVersion, newGroupVersion}
var codec = runtime.CodecFor(api.Scheme, testGroupVersion.String())
var newCodec = runtime.CodecFor(api.Scheme, newGroupVersion.String())
var accessor = meta.NewAccessor()
var versioner runtime.ResourceVersioner = accessor
@ -81,32 +78,37 @@ var admissionControl admission.Interface
var requestContextMapper api.RequestContextMapper
func interfacesFor(version string) (*meta.VersionInterfaces, error) {
switch version {
case testVersion:
gv, err := unversioned.ParseGroupVersion(version)
if err != nil {
return nil, err
}
switch gv {
case testGroupVersion:
return &meta.VersionInterfaces{
Codec: codec,
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
case newVersion:
case newGroupVersion:
return &meta.VersionInterfaces{
Codec: newCodec,
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
case grouplessVersion:
case grouplessGroupVersion:
return &meta.VersionInterfaces{
Codec: grouplessCodec,
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
default:
return nil, fmt.Errorf("unsupported storage version: %s (valid: %s)", version, strings.Join(versions, ", "))
return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, groupVersions)
}
}
func newMapper() *meta.DefaultRESTMapper {
return meta.NewDefaultRESTMapper("testgroup", versions, interfacesFor)
gvStrings := []string{testGroupVersion.String(), newGroupVersion.String()}
return meta.NewDefaultRESTMapper(testAPIGroup, gvStrings, interfacesFor)
}
func addGrouplessTypes() {
@ -120,9 +122,9 @@ func addGrouplessTypes() {
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(
grouplessVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
grouplessGroupVersion.String(), &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
&ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
api.Scheme.AddKnownTypes(grouplessVersion, &api.Pod{})
api.Scheme.AddKnownTypes(grouplessGroupVersion.String(), &api.Pod{})
}
func addTestTypes() {
@ -136,9 +138,9 @@ func addTestTypes() {
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(
testVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
testGroupVersion.String(), &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
&ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
api.Scheme.AddKnownTypes(testVersion, &api.Pod{})
api.Scheme.AddKnownTypes(testGroupVersion.String(), &api.Pod{})
}
func addNewTestTypes() {
@ -152,7 +154,7 @@ func addNewTestTypes() {
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(
newVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
newGroupVersion.String(), &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
&ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
}
@ -172,13 +174,13 @@ func init() {
// enumerate all supported versions, get the kinds, and register with
// the mapper how to address our resources
for _, version := range versions {
for kind := range api.Scheme.KnownTypes(version) {
for _, gv := range groupVersions {
for kind := range api.Scheme.KnownTypes(gv.String()) {
root := bool(kind == "SimpleRoot")
if root {
nsMapper.Add(meta.RESTScopeRoot, kind, version, false)
nsMapper.Add(meta.RESTScopeRoot, kind, gv.String(), false)
} else {
nsMapper.Add(meta.RESTScopeNamespace, kind, version, false)
nsMapper.Add(meta.RESTScopeNamespace, kind, gv.String(), false)
}
}
}
@ -188,17 +190,17 @@ func init() {
admissionControl = admit.NewAlwaysAdmit()
requestContextMapper = api.NewRequestContextMapper()
api.Scheme.AddFieldLabelConversionFunc(grouplessVersion, "Simple",
api.Scheme.AddFieldLabelConversionFunc(grouplessGroupVersion.String(), "Simple",
func(label, value string) (string, string, error) {
return label, value, nil
},
)
api.Scheme.AddFieldLabelConversionFunc(testVersion, "Simple",
api.Scheme.AddFieldLabelConversionFunc(testGroupVersion.String(), "Simple",
func(label, value string) (string, string, error) {
return label, value, nil
},
)
api.Scheme.AddFieldLabelConversionFunc(newVersion, "Simple",
api.Scheme.AddFieldLabelConversionFunc(newGroupVersion.String(), "Simple",
func(label, value string) (string, string, error) {
return label, value, nil
},
@ -653,31 +655,31 @@ func TestNotFound(t *testing.T) {
}
cases := map[string]T{
// Positive checks to make sure everything is wired correctly
"groupless GET root": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/simpleroots", http.StatusOK},
"groupless GET namespaced": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/ns/simples", http.StatusOK},
"groupless GET root": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusOK},
"groupless GET namespaced": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusOK},
"groupless GET long prefix": {"GET", "/" + grouplessPrefix + "/", http.StatusNotFound},
"groupless root PATCH method": {"PATCH", "/" + grouplessPrefix + "/" + grouplessVersion + "/simpleroots", http.StatusMethodNotAllowed},
"groupless root GET missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/blah", http.StatusNotFound},
"groupless root GET with extra segment": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/simpleroots/bar/baz", http.StatusNotFound},
"groupless root DELETE without extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessVersion + "/simpleroots", http.StatusMethodNotAllowed},
"groupless root DELETE with extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessVersion + "/simpleroots/bar/baz", http.StatusNotFound},
"groupless root PUT without extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessVersion + "/simpleroots", http.StatusMethodNotAllowed},
"groupless root PUT with extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessVersion + "/simpleroots/bar/baz", http.StatusNotFound},
"groupless root watch missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/watch/", http.StatusNotFound},
"groupless root PATCH method": {"PATCH", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
"groupless root GET missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/blah", http.StatusNotFound},
"groupless root GET with extra segment": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
"groupless root DELETE without extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
"groupless root DELETE with extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
"groupless root PUT without extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
"groupless root PUT with extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
"groupless root watch missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusNotFound},
"groupless namespaced PATCH method": {"PATCH", "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
"groupless namespaced PATCH method": {"PATCH", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
"groupless namespaced GET long prefix": {"GET", "/" + grouplessPrefix + "/", http.StatusNotFound},
"groupless namespaced GET missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/blah", http.StatusNotFound},
"groupless namespaced GET with extra segment": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
"groupless namespaced POST with extra segment": {"POST", "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
"groupless namespaced DELETE without extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
"groupless namespaced DELETE with extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
"groupless namespaced PUT without extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
"groupless namespaced PUT with extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
"groupless namespaced watch missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/watch/", http.StatusNotFound},
"groupless namespaced watch with bad method": {"POST", "/" + grouplessPrefix + "/" + grouplessVersion + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
"groupless namespaced GET missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/blah", http.StatusNotFound},
"groupless namespaced GET with extra segment": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
"groupless namespaced POST with extra segment": {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
"groupless namespaced DELETE without extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
"groupless namespaced DELETE with extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
"groupless namespaced PUT without extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
"groupless namespaced PUT with extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
"groupless namespaced watch missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusNotFound},
"groupless namespaced watch with bad method": {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
// Positive checks to make sure everything is wired correctly
"GET root": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusOK},
@ -753,15 +755,15 @@ func TestUnimplementedRESTStorage(t *testing.T) {
ErrCode int
}
cases := map[string]T{
"groupless GET object": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/foo/bar", http.StatusNotFound},
"groupless GET list": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/foo", http.StatusNotFound},
"groupless POST list": {"POST", "/" + grouplessPrefix + "/" + grouplessVersion + "/foo", http.StatusNotFound},
"groupless PUT object": {"PUT", "/" + grouplessPrefix + "/" + grouplessVersion + "/foo/bar", http.StatusNotFound},
"groupless DELETE object": {"DELETE", "/" + grouplessPrefix + "/" + grouplessVersion + "/foo/bar", http.StatusNotFound},
"groupless watch list": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/watch/foo", http.StatusNotFound},
"groupless watch object": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/watch/foo/bar", http.StatusNotFound},
"groupless proxy object": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/proxy/foo/bar", http.StatusNotFound},
"groupless redirect object": {"GET", "/" + grouplessPrefix + "/" + grouplessVersion + "/redirect/foo/bar", http.StatusNotFound},
"groupless GET object": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
"groupless GET list": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo", http.StatusNotFound},
"groupless POST list": {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo", http.StatusNotFound},
"groupless PUT object": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
"groupless DELETE object": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
"groupless watch list": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/foo", http.StatusNotFound},
"groupless watch object": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/foo/bar", http.StatusNotFound},
"groupless proxy object": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/proxy/foo/bar", http.StatusNotFound},
"groupless redirect object": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/redirect/foo/bar", http.StatusNotFound},
"GET object": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo/bar", http.StatusNotFound},
"GET list": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo", http.StatusNotFound},
@ -841,76 +843,76 @@ func TestList(t *testing.T) {
// legacy namespace param is ignored
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple?namespace=",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=",
namespace: "",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
legacy: true,
},
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple?namespace=other",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=other",
namespace: "",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
legacy: true,
},
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple?namespace=other&labelSelector=a%3Db&fieldSelector=c%3Dd",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=other&labelSelector=a%3Db&fieldSelector=c%3Dd",
namespace: "",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
legacy: true,
label: "a=b",
field: "c=d",
},
// legacy api version is honored
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
namespace: "",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
legacy: true,
},
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/other/simple",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
namespace: "other",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/other/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
legacy: true,
},
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
namespace: "other",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/other/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
legacy: true,
label: "a=b",
field: "c=d",
},
// list items across all namespaces
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
namespace: "",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
legacy: true,
},
// list items in a namespace in the path
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/default/simple",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
namespace: "default",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/default/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
},
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/other/simple",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
namespace: "other",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/other/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
},
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
namespace: "other",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/namespaces/other/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
label: "a=b",
field: "c=d",
},
// list items across all namespaces
{
url: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
namespace: "",
selfLink: "/" + grouplessPrefix + "/" + grouplessVersion + "/simple",
selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
},
// Group API
@ -2862,7 +2864,7 @@ func TestCreateChecksAPIVersion(t *testing.T) {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testVersion+"/namespaces/default/simple", bytes.NewBuffer(data))
request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -2903,7 +2905,7 @@ func TestCreateDefaultsAPIVersion(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testVersion+"/namespaces/default/simple", bytes.NewBuffer(data))
request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -2927,7 +2929,7 @@ func TestUpdateChecksAPIVersion(t *testing.T) {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testVersion+"/namespaces/default/simple/bar", bytes.NewBuffer(data))
request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/bar", bytes.NewBuffer(data))
if err != nil {
t.Errorf("unexpected error: %v", err)
}

View File

@ -178,8 +178,8 @@ func TestWatchParamParsing(t *testing.T) {
dest, _ := url.Parse(server.URL)
rootPath := "/" + prefix + "/" + testVersion + "/watch/simples"
namespacedPath := "/" + prefix + "/" + testVersion + "/watch/namespaces/other/simpleroots"
rootPath := "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/simples"
namespacedPath := "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/namespaces/other/simpleroots"
table := []struct {
path string
@ -358,13 +358,13 @@ func TestWatchHTTPTimeout(t *testing.T) {
// Setup a client
dest, _ := url.Parse(s.URL)
dest.Path = "/" + prefix + "/" + newVersion + "/simple"
dest.Path = "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/simple"
dest.RawQuery = "watch=true"
req, _ := http.NewRequest("GET", dest.String(), nil)
client := http.Client{}
resp, err := client.Do(req)
watcher.Add(&apiservertesting.Simple{TypeMeta: unversioned.TypeMeta{APIVersion: newVersion}})
watcher.Add(&apiservertesting.Simple{TypeMeta: unversioned.TypeMeta{APIVersion: newGroupVersion.String()}})
// Make sure we can actually watch an endpoint
decoder := json.NewDecoder(resp.Body)

View File

@ -96,7 +96,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
}
info := infos[0]
mapping := info.ResourceMapping()
if err := f.CanBeAutoscaled(mapping.Kind); err != nil {
if err := f.CanBeAutoscaled(mapping.GroupVersionKind.Kind); err != nil {
return err
}
@ -111,9 +111,9 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
name := info.Name
params["default-name"] = name
params["scaleRef-kind"] = mapping.Kind
params["scaleRef-kind"] = mapping.GroupVersionKind.Kind
params["scaleRef-name"] = name
params["scaleRef-apiVersion"] = mapping.APIVersion
params["scaleRef-apiVersion"] = mapping.GroupVersionKind.GroupVersion().String()
if err = kubectl.ValidateParams(names, params); err != nil {
return err

View File

@ -76,23 +76,27 @@ func versionErrIfFalse(b bool) error {
return versionErr
}
var validVersion = testapi.Default.Version()
var internalGV = unversioned.GroupVersion{Group: "apitest", Version: ""}
var unlikelyGV = unversioned.GroupVersion{Group: "apitest", Version: "unlikelyversion"}
var validVersionGV = unversioned.GroupVersion{Group: "apitest", Version: validVersion}
func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "Type", &internalType{})
scheme.AddKnownTypeWithName("unlikelyversion", "Type", &externalType{})
scheme.AddKnownTypeWithName(internalGV.Version, "Type", &internalType{})
scheme.AddKnownTypeWithName(unlikelyGV.String(), "Type", &externalType{})
//This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
scheme.AddKnownTypeWithName(testapi.Default.Version(), "Type", &ExternalType2{})
scheme.AddKnownTypeWithName(validVersionGV.String(), "Type", &ExternalType2{})
codec := runtime.CodecFor(scheme, "unlikelyversion")
validVersion := testapi.Default.Version()
mapper := meta.NewDefaultRESTMapper("apitest", []string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, error) {
codec := runtime.CodecFor(scheme, unlikelyGV.String())
mapper := meta.NewDefaultRESTMapper("apitest", []string{unlikelyGV.String(), validVersionGV.String()}, func(version string) (*meta.VersionInterfaces, error) {
return &meta.VersionInterfaces{
Codec: runtime.CodecFor(scheme, version),
ObjectConvertor: scheme,
MetadataAccessor: meta.NewAccessor(),
}, versionErrIfFalse(version == validVersion || version == "unlikelyversion")
}, versionErrIfFalse(version == validVersionGV.String() || version == unlikelyGV.String())
})
for _, version := range []string{"unlikelyversion", validVersion} {
for _, version := range []string{unlikelyGV.String(), validVersionGV.String()} {
for kind := range scheme.KnownTypes(version) {
mixedCase := false
scope := meta.RESTScopeNamespace

View File

@ -120,7 +120,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
}
info := infos[0]
mapping := info.ResourceMapping()
if err := f.CanBeExposed(mapping.Kind); err != nil {
if err := f.CanBeExposed(mapping.GroupVersionKind.Kind); err != nil {
return err
}
// Get the input object
@ -187,7 +187,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
}
if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 {
object, err = cmdutil.Merge(object, inline, mapping.Kind)
object, err = cmdutil.Merge(object, inline, mapping.GroupVersionKind.Kind)
if err != nil {
return err
}

View File

@ -159,19 +159,19 @@ func TestGetUnknownSchemaObjectListGeneric(t *testing.T) {
"handles specific version": {
outputVersion: testapi.Default.Version(),
listVersion: testapi.Default.Version(),
testtypeVersion: "unlikelyversion",
testtypeVersion: unlikelyGV.String(),
rcVersion: testapi.Default.Version(),
},
"handles second specific version": {
outputVersion: "unlikelyversion",
listVersion: testapi.Default.Version(),
testtypeVersion: "unlikelyversion",
testtypeVersion: unlikelyGV.String(),
rcVersion: testapi.Default.Version(), // see expected behavior 3b
},
"handles common version": {
outputVersion: testapi.Default.Version(),
listVersion: testapi.Default.Version(),
testtypeVersion: "unlikelyversion",
testtypeVersion: unlikelyGV.String(),
rcVersion: testapi.Default.Version(),
},
}
@ -198,6 +198,7 @@ func TestGetUnknownSchemaObjectListGeneric(t *testing.T) {
cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf)
cmd.Flags().Set("output", "json")
cmd.Flags().Set("output-version", test.outputVersion)
err := RunGet(f, buf, cmd, []string{"type/foo", "replicationcontrollers/foo"}, &GetOptions{})
if err != nil {

View File

@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -382,8 +383,9 @@ func isReplicasDefaulted(info *resource.Info) bool {
// was unable to recover versioned info
return false
}
switch info.Mapping.APIVersion {
case "v1":
switch info.Mapping.GroupVersionKind.GroupVersion() {
case unversioned.GroupVersion{Version: "v1"}:
if rc, ok := info.VersionedObject.(*v1.ReplicationController); ok {
return rc.Spec.Replicas == nil
}

View File

@ -148,7 +148,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
if err != nil {
return nil, err
}
client, err := clients.ClientForVersion(mapping.APIVersion)
client, err := clients.ClientForVersion(mapping.GroupVersionKind.GroupVersion().String())
if err != nil {
return nil, err
}
@ -165,11 +165,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
if err != nil {
return nil, err
}
client, err := clients.ClientForVersion(mapping.APIVersion)
client, err := clients.ClientForVersion(mapping.GroupVersionKind.GroupVersion().String())
if err != nil {
return nil, err
}
if describer, ok := kubectl.DescriberFor(group, mapping.Kind, client); ok {
if describer, ok := kubectl.DescriberFor(group, mapping.GroupVersionKind.Kind, client); ok {
return describer, nil
}
return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
@ -242,18 +242,18 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
},
Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
client, err := clients.ClientForVersion(mapping.GroupVersionKind.GroupVersion().String())
if err != nil {
return nil, err
}
return kubectl.ScalerFor(mapping.Kind, client)
return kubectl.ScalerFor(mapping.GroupVersionKind.Kind, client)
},
Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
client, err := clients.ClientForVersion(mapping.GroupVersionKind.GroupVersion().String())
if err != nil {
return nil, err
}
return kubectl.ReaperFor(mapping.Kind, client)
return kubectl.ReaperFor(mapping.GroupVersionKind.Kind, client)
},
Validator: func(validate bool, cacheDir string) (validation.Schema, error) {
if validate {
@ -581,12 +581,12 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin
version := OutputVersion(cmd, defaultVersion)
if len(version) == 0 {
version = mapping.APIVersion
version = mapping.GroupVersionKind.GroupVersion().String()
}
if len(version) == 0 {
return nil, fmt.Errorf("you must specify an output-version when using this output format")
}
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.APIVersion)
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion().String())
} else {
// Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper
columnLabel, err := cmd.Flags().GetStringSlice("label-columns")

View File

@ -470,7 +470,7 @@ func TestResourceByNameWithoutRequireObject(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if mapping.Kind != "Pod" || mapping.Resource != "pods" {
if mapping.GroupVersionKind.Kind != "Pod" || mapping.Resource != "pods" {
t.Errorf("unexpected resource mapping: %#v", mapping)
}
}

View File

@ -254,7 +254,7 @@ func AsVersionedObjects(infos []*Info, version string) ([]runtime.Object, error)
}
}
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.APIVersion)
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion().String())
if err != nil {
return nil, err
}

View File

@ -45,7 +45,7 @@ func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace string,
// Visit implements Visitor
func (r *Selector) Visit(fn VisitorFunc) error {
list, err := NewHelper(r.Client, r.Mapping).List(r.Namespace, r.ResourceMapping().APIVersion, r.Selector)
list, err := NewHelper(r.Client, r.Mapping).List(r.Namespace, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.Selector)
if err != nil {
if errors.IsBadRequest(err) || errors.IsNotFound(err) {
if r.Selector.Empty() {
@ -70,7 +70,7 @@ func (r *Selector) Visit(fn VisitorFunc) error {
}
func (r *Selector) Watch(resourceVersion string) (watch.Interface, error) {
return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, resourceVersion, r.ResourceMapping().APIVersion, r.Selector)
return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, resourceVersion, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.Selector)
}
// ResourceMapping returns the mapping for this resource and implements ResourceMapping