mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
RESTMapper should take into account multiple versions
When a CLI command `kubectl get rc --api-version=v1beta3` is called, the API resource name should match v1beta3, not whatever the default RESTMapper version is. This allows the correct resource name to be returned ("replicationcontrollers", instead of "replicationControllers").
This commit is contained in:
parent
9b6aec5e22
commit
8bef68d475
@ -228,7 +228,7 @@ func TestRESTMapper(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, version := range Versions {
|
for _, version := range Versions {
|
||||||
mapping, err := RESTMapper.RESTMapping(version, "ReplicationController")
|
mapping, err := RESTMapper.RESTMapping("ReplicationController", version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -106,5 +106,5 @@ type RESTMapping struct {
|
|||||||
// consumers of Kubernetes compatible REST APIs as defined in docs/api-conventions.md.
|
// consumers of Kubernetes compatible REST APIs as defined in docs/api-conventions.md.
|
||||||
type RESTMapper interface {
|
type RESTMapper interface {
|
||||||
VersionAndKindForResource(resource string) (defaultVersion, kind string, err error)
|
VersionAndKindForResource(resource string) (defaultVersion, kind string, err error)
|
||||||
RESTMapping(version, kind string) (*RESTMapping, error)
|
RESTMapping(kind string, versions ...string) (*RESTMapping, error)
|
||||||
}
|
}
|
||||||
|
@ -122,22 +122,35 @@ func (m *DefaultRESTMapper) VersionAndKindForResource(resource string) (defaultV
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RESTMapping returns a struct representing the resource path and conversion interfaces a
|
// RESTMapping returns a struct representing the resource path and conversion interfaces a
|
||||||
// RESTClient should use to operate on the provided version and kind. If a version is not
|
// RESTClient should use to operate on the provided kind in order of versions. If a version search
|
||||||
// provided, the search order provided to DefaultRESTMapper will be used to resolve which
|
// 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.
|
// APIVersion should be used to access the named kind.
|
||||||
func (m *DefaultRESTMapper) RESTMapping(version, kind string) (*RESTMapping, error) {
|
func (m *DefaultRESTMapper) RESTMapping(kind string, versions ...string) (*RESTMapping, error) {
|
||||||
// Default to a version with this kind
|
// Pick an appropriate version
|
||||||
if len(version) == 0 {
|
var version string
|
||||||
|
hadVersion := false
|
||||||
|
for _, v := range versions {
|
||||||
|
if len(v) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hadVersion = true
|
||||||
|
if _, ok := m.reverse[typeMeta{APIVersion: v, Kind: kind}]; ok {
|
||||||
|
version = v
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Use the default preferred versions
|
||||||
|
if !hadVersion && len(version) == 0 {
|
||||||
for _, v := range m.versions {
|
for _, v := range m.versions {
|
||||||
if _, ok := m.reverse[typeMeta{APIVersion: v, Kind: kind}]; ok {
|
if _, ok := m.reverse[typeMeta{APIVersion: v, Kind: kind}]; ok {
|
||||||
version = v
|
version = v
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if len(version) == 0 {
|
if len(version) == 0 {
|
||||||
return nil, fmt.Errorf("no object named %q is registered", kind)
|
return nil, fmt.Errorf("no object named %q is registered", kind)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we have a REST mapping
|
// Ensure we have a REST mapping
|
||||||
resource, ok := m.reverse[typeMeta{APIVersion: version, Kind: kind}]
|
resource, ok := m.reverse[typeMeta{APIVersion: version, Kind: kind}]
|
||||||
|
@ -122,31 +122,39 @@ func TestKindToResource(t *testing.T) {
|
|||||||
|
|
||||||
func TestRESTMapperRESTMapping(t *testing.T) {
|
func TestRESTMapperRESTMapping(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Kind, APIVersion string
|
Kind string
|
||||||
|
APIVersions []string
|
||||||
MixedCase bool
|
MixedCase bool
|
||||||
|
DefaultVersions []string
|
||||||
|
|
||||||
Resource string
|
Resource string
|
||||||
Version string
|
Version string
|
||||||
Err bool
|
Err bool
|
||||||
}{
|
}{
|
||||||
{Kind: "Unknown", APIVersion: "", Err: true},
|
{Kind: "Unknown", Err: true},
|
||||||
|
{Kind: "InternalObject", Err: true},
|
||||||
|
|
||||||
{Kind: "InternalObject", APIVersion: "test", Resource: "internalobjects"},
|
{DefaultVersions: []string{"test"}, Kind: "Unknown", Err: true},
|
||||||
{Kind: "InternalObject", APIVersion: "test", Resource: "internalobjects"},
|
|
||||||
{Kind: "InternalObject", APIVersion: "", Resource: "internalobjects", Version: "test"},
|
|
||||||
|
|
||||||
{Kind: "InternalObject", APIVersion: "test", Resource: "internalobjects"},
|
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
|
||||||
{Kind: "InternalObject", APIVersion: "test", MixedCase: true, Resource: "internalObjects"},
|
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
|
||||||
|
|
||||||
|
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
|
||||||
|
|
||||||
|
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{}, Resource: "internalobjects", Version: "test"},
|
||||||
|
|
||||||
|
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
|
||||||
|
{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, MixedCase: true, Resource: "internalObjects"},
|
||||||
|
|
||||||
// TODO: add test for a resource that exists in one version but not another
|
// TODO: add test for a resource that exists in one version but not another
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
|
mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
scheme.AddKnownTypes("test", &InternalObject{})
|
scheme.AddKnownTypes("test", &InternalObject{})
|
||||||
mapper.Add(scheme, testCase.MixedCase, "test")
|
mapper.Add(scheme, testCase.MixedCase, "test")
|
||||||
|
|
||||||
mapping, err := mapper.RESTMapping(testCase.APIVersion, testCase.Kind)
|
mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
|
||||||
hasErr := err != nil
|
hasErr := err != nil
|
||||||
if hasErr != testCase.Err {
|
if hasErr != testCase.Err {
|
||||||
t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
|
t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
|
||||||
@ -159,7 +167,7 @@ func TestRESTMapperRESTMapping(t *testing.T) {
|
|||||||
}
|
}
|
||||||
version := testCase.Version
|
version := testCase.Version
|
||||||
if version == "" {
|
if version == "" {
|
||||||
version = testCase.APIVersion
|
version = testCase.APIVersions[0]
|
||||||
}
|
}
|
||||||
if mapping.APIVersion != version {
|
if mapping.APIVersion != version {
|
||||||
t.Errorf("%d: unexpected version: %#v", i, mapping)
|
t.Errorf("%d: unexpected version: %#v", i, mapping)
|
||||||
@ -179,37 +187,51 @@ func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
|
|||||||
mapper.Add(scheme, false, "test1", "test2")
|
mapper.Add(scheme, false, "test1", "test2")
|
||||||
|
|
||||||
// pick default matching object kind based on search order
|
// pick default matching object kind based on search order
|
||||||
mapping, err := mapper.RESTMapping("", "OtherObject")
|
mapping, err := mapper.RESTMapping("OtherObject")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
|
if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
|
||||||
t.Errorf("unexpected mapping: %#v", mapping)
|
t.Errorf("unexpected mapping: %#v", mapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping, err = mapper.RESTMapping("", "InternalObject")
|
mapping, err = mapper.RESTMapping("InternalObject")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if mapping.Resource != "internalobjects" || mapping.APIVersion != "test1" {
|
if mapping.Resource != "internalobjects" || mapping.APIVersion != "test1" {
|
||||||
t.Errorf("unexpected mapping: %#v", mapping)
|
t.Errorf("unexpected mapping: %#v", mapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mismatch of version
|
// mismatch of version
|
||||||
mapping, err = mapper.RESTMapping("test2", "InternalObject")
|
mapping, err = mapper.RESTMapping("InternalObject", "test2")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("unexpected non-error")
|
t.Errorf("unexpected non-error")
|
||||||
}
|
}
|
||||||
mapping, err = mapper.RESTMapping("test1", "OtherObject")
|
mapping, err = mapper.RESTMapping("OtherObject", "test1")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("unexpected non-error")
|
t.Errorf("unexpected non-error")
|
||||||
}
|
}
|
||||||
|
|
||||||
// not in the search versions
|
// not in the search versions
|
||||||
mapping, err = mapper.RESTMapping("test3", "OtherObject")
|
mapping, err = mapper.RESTMapping("OtherObject", "test3")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("unexpected non-error")
|
t.Errorf("unexpected non-error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// explicit search order
|
||||||
|
mapping, err = mapper.RESTMapping("OtherObject", "test3", "test1")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("unexpected non-error")
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping, err = mapper.RESTMapping("OtherObject", "test3", "test2")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected non-error")
|
||||||
|
}
|
||||||
|
if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
|
||||||
|
t.Errorf("unexpected mapping: %#v", mapping)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
|
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
|
||||||
@ -218,7 +240,7 @@ func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
|
|||||||
scheme.AddKnownTypes("test1", &InternalObject{})
|
scheme.AddKnownTypes("test1", &InternalObject{})
|
||||||
mapper.Add(scheme, false, "test1")
|
mapper.Add(scheme, false, "test1")
|
||||||
|
|
||||||
_, err := mapper.RESTMapping("test1", "InternalObject")
|
_, err := mapper.RESTMapping("InternalObject", "test1")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("unexpected non-error")
|
t.Errorf("unexpected non-error")
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ func CreateObjects(typer runtime.ObjectTyper, mapper meta.RESTMapper, clientFor
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping, err := mapper.RESTMapping(version, kind)
|
mapping, err := mapper.RESTMapping(kind, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
allErrors = append(allErrors, fmt.Errorf("Config.item[%d] mapping: %v", i, err))
|
allErrors = append(allErrors, fmt.Errorf("Config.item[%d] mapping: %v", i, err))
|
||||||
continue
|
continue
|
||||||
|
@ -44,7 +44,7 @@ func DataToObjects(m meta.RESTMapper, t runtime.ObjectTyper, data []byte) (resul
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping, err := m.RESTMapping(version, kind)
|
mapping, err := m.RESTMapping(kind, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = append(errors, fmt.Errorf("item[%d] mapping: %v", i, err))
|
errors = append(errors, fmt.Errorf("item[%d] mapping: %v", i, err))
|
||||||
continue
|
continue
|
||||||
|
@ -37,6 +37,7 @@ func TestDescribeUnknownSchemaObject(t *testing.T) {
|
|||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
cmd := f.NewCmdDescribe(buf)
|
cmd := f.NewCmdDescribe(buf)
|
||||||
|
cmd.Flags().String("api-version", "default", "")
|
||||||
cmd.Flags().String("namespace", "test", "")
|
cmd.Flags().String("namespace", "test", "")
|
||||||
cmd.Run(cmd, []string{"type", "foo"})
|
cmd.Run(cmd, []string{"type", "foo"})
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ func TestGetUnknownSchemaObject(t *testing.T) {
|
|||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
cmd := f.NewCmdGet(buf)
|
cmd := f.NewCmdGet(buf)
|
||||||
|
cmd.Flags().String("api-version", "default", "")
|
||||||
cmd.Flags().String("namespace", "test", "")
|
cmd.Flags().String("namespace", "test", "")
|
||||||
cmd.Run(cmd, []string{"type", "foo"})
|
cmd.Run(cmd, []string{"type", "foo"})
|
||||||
|
|
||||||
|
@ -201,13 +201,13 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
|
|||||||
usageError(cmd, "Must specify filename or command line params")
|
usageError(cmd, "Must specify filename or command line params")
|
||||||
}
|
}
|
||||||
|
|
||||||
version, kind, err := mapper.VersionAndKindForResource(resource)
|
defaultVersion, kind, err := mapper.VersionAndKindForResource(resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The error returned by mapper is "no resource defined", which is a usage error
|
// The error returned by mapper is "no resource defined", which is a usage error
|
||||||
usageError(cmd, err.Error())
|
usageError(cmd, err.Error())
|
||||||
}
|
}
|
||||||
|
version := GetFlagString(cmd, "api-version")
|
||||||
mapping, err = mapper.RESTMapping(version, kind)
|
mapping, err = mapper.RESTMapping(kind, version, defaultVersion)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -242,7 +242,7 @@ func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper)
|
|||||||
version, kind, err := mapper.VersionAndKindForResource(resource)
|
version, kind, err := mapper.VersionAndKindForResource(resource)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
mapping, err = mapper.RESTMapping(version, kind)
|
mapping, err = mapper.RESTMapping(kind, version)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -268,10 +268,11 @@ func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version, kind, err := mapper.VersionAndKindForResource(resource)
|
defaultVersion, kind, err := mapper.VersionAndKindForResource(resource)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
mapping, err = mapper.RESTMapping(version, kind)
|
version := GetFlagString(cmd, "api-version")
|
||||||
|
mapping, err = mapper.RESTMapping(kind, version, defaultVersion)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -296,7 +297,7 @@ func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RE
|
|||||||
err = schema.ValidateBytes(data)
|
err = schema.ValidateBytes(data)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
mapping, err = mapper.RESTMapping(version, kind)
|
mapping, err = mapper.RESTMapping(kind, version)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
obj, err := mapping.Codec.Decode(data)
|
obj, err := mapping.Codec.Decode(data)
|
||||||
|
Loading…
Reference in New Issue
Block a user