mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
Merge pull request #20814 from deads2k/restmapper-errors
Auto commit by PR queue bot
This commit is contained in:
commit
53d9f9ecab
@ -281,6 +281,27 @@ runTests() {
|
||||
fi
|
||||
stop-proxy
|
||||
|
||||
#########################
|
||||
# RESTMapper evaluation #
|
||||
#########################
|
||||
|
||||
kube::log::status "Testing RESTMapper"
|
||||
|
||||
RESTMAPPER_ERROR_FILE="${KUBE_TEMP}/restmapper-error"
|
||||
|
||||
### Non-existent resource type should give a recognizeable error
|
||||
# Pre-condition: None
|
||||
# Command
|
||||
kubectl get "${kube_flags[@]}" unknownresourcetype 2>${RESTMAPPER_ERROR_FILE} || true
|
||||
if grep -q "the server doesn't have a resource type" "${RESTMAPPER_ERROR_FILE}"; then
|
||||
kube::log::status "\"kubectl get unknownresourcetype\" returns error as expected: $(cat ${RESTMAPPER_ERROR_FILE})"
|
||||
else
|
||||
kube::log::status "\"kubectl get unknownresourcetype\" returns unexpected error or non-error: $(cat ${RESTMAPPER_ERROR_FILE})"
|
||||
exit 1
|
||||
fi
|
||||
rm "${RESTMAPPER_ERROR_FILE}"
|
||||
# Post-condition: None
|
||||
|
||||
###########################
|
||||
# POD creation / deletion #
|
||||
###########################
|
||||
|
72
pkg/api/meta/errors.go
Normal file
72
pkg/api/meta/errors.go
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package meta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
// AmbiguousResourceError is returned if the RESTMapper finds multiple matches for a resource
|
||||
type AmbiguousResourceError struct {
|
||||
PartialResource unversioned.GroupVersionResource
|
||||
|
||||
MatchingResources []unversioned.GroupVersionResource
|
||||
MatchingKinds []unversioned.GroupVersionKind
|
||||
}
|
||||
|
||||
func (e *AmbiguousResourceError) Error() string {
|
||||
switch {
|
||||
case len(e.MatchingKinds) > 0 && len(e.MatchingResources) > 0:
|
||||
return fmt.Sprintf("%v matches multiple resources %v and kinds %v", e.PartialResource, e.MatchingResources, e.MatchingKinds)
|
||||
case len(e.MatchingKinds) > 0:
|
||||
return fmt.Sprintf("%v matches multiple kinds %v", e.PartialResource, e.MatchingKinds)
|
||||
case len(e.MatchingResources) > 0:
|
||||
return fmt.Sprintf("%v matches multiple resources %v", e.PartialResource, e.MatchingResources)
|
||||
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v matches multiple resources or kinds", e.PartialResource)
|
||||
}
|
||||
|
||||
func IsAmbiguousResourceError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := err.(*AmbiguousResourceError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// NoResourceMatchError is returned if the RESTMapper can't find any match for a resource
|
||||
type NoResourceMatchError struct {
|
||||
PartialResource unversioned.GroupVersionResource
|
||||
}
|
||||
|
||||
func (e *NoResourceMatchError) Error() string {
|
||||
return fmt.Sprintf("no matches for %v", e.PartialResource)
|
||||
}
|
||||
|
||||
func IsNoResourceMatchError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := err.(*NoResourceMatchError)
|
||||
return ok
|
||||
}
|
131
pkg/api/meta/multirestmapper.go
Normal file
131
pkg/api/meta/multirestmapper.go
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package meta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
// MultiRESTMapper is a wrapper for multiple RESTMappers.
|
||||
type MultiRESTMapper []RESTMapper
|
||||
|
||||
func (m MultiRESTMapper) String() string {
|
||||
nested := []string{}
|
||||
for _, t := range m {
|
||||
currString := fmt.Sprintf("%v", t)
|
||||
splitStrings := strings.Split(currString, "\n")
|
||||
nested = append(nested, strings.Join(splitStrings, "\n\t"))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("MultiRESTMapper{\n\t%s\n}", strings.Join(nested, "\n\t"))
|
||||
}
|
||||
|
||||
// ResourceSingularizer converts a REST resource name from plural to singular (e.g., from pods to pod)
|
||||
// This implementation supports multiple REST schemas and return the first match.
|
||||
func (m MultiRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
|
||||
for _, t := range m {
|
||||
singular, err = t.ResourceSingularizer(resource)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m MultiRESTMapper) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
|
||||
for _, t := range m {
|
||||
gvrs, err := t.ResourcesFor(resource)
|
||||
// ignore "no match" errors, but any other error percolates back up
|
||||
if !IsNoResourceMatchError(err) {
|
||||
return gvrs, err
|
||||
}
|
||||
}
|
||||
return nil, &NoResourceMatchError{PartialResource: resource}
|
||||
}
|
||||
|
||||
// KindsFor provides the Kind mappings for the REST resources. This implementation supports multiple REST schemas and returns
|
||||
// the first match.
|
||||
func (m MultiRESTMapper) KindsFor(resource unversioned.GroupVersionResource) (gvk []unversioned.GroupVersionKind, err error) {
|
||||
for _, t := range m {
|
||||
gvks, err := t.KindsFor(resource)
|
||||
// ignore "no match" errors, but any other error percolates back up
|
||||
if !IsNoResourceMatchError(err) {
|
||||
return gvks, err
|
||||
}
|
||||
}
|
||||
return nil, &NoResourceMatchError{PartialResource: resource}
|
||||
}
|
||||
|
||||
func (m MultiRESTMapper) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
|
||||
for _, t := range m {
|
||||
gvr, err := t.ResourceFor(resource)
|
||||
// ignore "no match" errors, but any other error percolates back up
|
||||
if !IsNoResourceMatchError(err) {
|
||||
return gvr, err
|
||||
}
|
||||
}
|
||||
return unversioned.GroupVersionResource{}, &NoResourceMatchError{PartialResource: resource}
|
||||
}
|
||||
|
||||
// KindsFor provides the Kind mapping for the REST resources. This implementation supports multiple REST schemas and returns
|
||||
// the first match.
|
||||
func (m MultiRESTMapper) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
|
||||
for _, t := range m {
|
||||
gvk, err := t.KindFor(resource)
|
||||
// ignore "no match" errors, but any other error percolates back up
|
||||
if !IsNoResourceMatchError(err) {
|
||||
return gvk, err
|
||||
}
|
||||
}
|
||||
return unversioned.GroupVersionKind{}, &NoResourceMatchError{PartialResource: resource}
|
||||
}
|
||||
|
||||
// RESTMapping provides the REST mapping for the resource based on the
|
||||
// kind and version. This implementation supports multiple REST schemas and
|
||||
// return the first match.
|
||||
func (m MultiRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
|
||||
for _, t := range m {
|
||||
mapping, err = t.RESTMapping(gk, versions...)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AliasesForResource finds the first alias response for the provided mappers.
|
||||
func (m MultiRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) {
|
||||
for _, t := range m {
|
||||
if aliases, ok = t.AliasesForResource(alias); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ResourceIsValid takes a string (either group/kind or kind) and checks if it's a valid resource
|
||||
func (m MultiRESTMapper) ResourceIsValid(resource unversioned.GroupVersionResource) bool {
|
||||
for _, t := range m {
|
||||
if t.ResourceIsValid(resource) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
238
pkg/api/meta/multirestmapper_test.go
Normal file
238
pkg/api/meta/multirestmapper_test.go
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package meta
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
func TestMultiRESTMapperResourceForErrorHandling(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
|
||||
mapper MultiRESTMapper
|
||||
input unversioned.GroupVersionResource
|
||||
result unversioned.GroupVersionResource
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
mapper: MultiRESTMapper{},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: unversioned.GroupVersionResource{},
|
||||
err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
},
|
||||
{
|
||||
name: "ignore not found",
|
||||
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: unversioned.GroupVersionResource{},
|
||||
err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
},
|
||||
{
|
||||
name: "accept first failure",
|
||||
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{resourceFor: unversioned.GroupVersionResource{Resource: "unused"}}},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: unversioned.GroupVersionResource{},
|
||||
err: errors.New("fail on this"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
actualResult, actualErr := tc.mapper.ResourceFor(tc.input)
|
||||
if e, a := tc.result, actualResult; e != a {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.err.Error(), actualErr.Error(); e != a {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiRESTMapperResourcesForErrorHandling(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
|
||||
mapper MultiRESTMapper
|
||||
input unversioned.GroupVersionResource
|
||||
result []unversioned.GroupVersionResource
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
mapper: MultiRESTMapper{},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: nil,
|
||||
err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
},
|
||||
{
|
||||
name: "ignore not found",
|
||||
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: nil,
|
||||
err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
},
|
||||
{
|
||||
name: "accept first failure",
|
||||
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{resourcesFor: []unversioned.GroupVersionResource{{Resource: "unused"}}}},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: nil,
|
||||
err: errors.New("fail on this"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
actualResult, actualErr := tc.mapper.ResourcesFor(tc.input)
|
||||
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.err.Error(), actualErr.Error(); e != a {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiRESTMapperKindsForErrorHandling(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
|
||||
mapper MultiRESTMapper
|
||||
input unversioned.GroupVersionResource
|
||||
result []unversioned.GroupVersionKind
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
mapper: MultiRESTMapper{},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: nil,
|
||||
err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
},
|
||||
{
|
||||
name: "ignore not found",
|
||||
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: nil,
|
||||
err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
},
|
||||
{
|
||||
name: "accept first failure",
|
||||
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{kindsFor: []unversioned.GroupVersionKind{{Kind: "unused"}}}},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: nil,
|
||||
err: errors.New("fail on this"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
actualResult, actualErr := tc.mapper.KindsFor(tc.input)
|
||||
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.err.Error(), actualErr.Error(); e != a {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiRESTMapperKindForErrorHandling(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
|
||||
mapper MultiRESTMapper
|
||||
input unversioned.GroupVersionResource
|
||||
result unversioned.GroupVersionKind
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
mapper: MultiRESTMapper{},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: unversioned.GroupVersionKind{},
|
||||
err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
},
|
||||
{
|
||||
name: "ignore not found",
|
||||
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: unversioned.GroupVersionKind{},
|
||||
err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
},
|
||||
{
|
||||
name: "accept first failure",
|
||||
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{kindFor: unversioned.GroupVersionKind{Kind: "unused"}}},
|
||||
input: unversioned.GroupVersionResource{Resource: "foo"},
|
||||
result: unversioned.GroupVersionKind{},
|
||||
err: errors.New("fail on this"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
actualResult, actualErr := tc.mapper.KindFor(tc.input)
|
||||
if e, a := tc.result, actualResult; e != a {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.err.Error(), actualErr.Error(); e != a {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fixedRESTMapper struct {
|
||||
resourcesFor []unversioned.GroupVersionResource
|
||||
kindsFor []unversioned.GroupVersionKind
|
||||
resourceFor unversioned.GroupVersionResource
|
||||
kindFor unversioned.GroupVersionKind
|
||||
|
||||
err error
|
||||
}
|
||||
|
||||
func (m fixedRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
|
||||
return "", m.err
|
||||
}
|
||||
|
||||
func (m fixedRESTMapper) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
|
||||
return m.resourcesFor, m.err
|
||||
}
|
||||
|
||||
func (m fixedRESTMapper) KindsFor(resource unversioned.GroupVersionResource) (gvk []unversioned.GroupVersionKind, err error) {
|
||||
return m.kindsFor, m.err
|
||||
}
|
||||
|
||||
func (m fixedRESTMapper) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
|
||||
return m.resourceFor, m.err
|
||||
}
|
||||
|
||||
func (m fixedRESTMapper) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
|
||||
return m.kindFor, m.err
|
||||
}
|
||||
|
||||
func (m fixedRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
|
||||
return nil, m.err
|
||||
}
|
||||
|
||||
func (m fixedRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m fixedRESTMapper) ResourceIsValid(resource unversioned.GroupVersionResource) bool {
|
||||
return false
|
||||
}
|
@ -243,7 +243,7 @@ func (m *DefaultRESTMapper) ResourcesFor(resource unversioned.GroupVersionResour
|
||||
}
|
||||
|
||||
if len(ret) == 0 {
|
||||
return nil, fmt.Errorf("no resource %v has been defined; known resources: %v", resource, m.pluralToSingular)
|
||||
return nil, &NoResourceMatchError{PartialResource: resource}
|
||||
}
|
||||
|
||||
sort.Sort(resourceByPreferredGroupVersion{ret, m.defaultGroupVersions})
|
||||
@ -259,7 +259,7 @@ func (m *DefaultRESTMapper) ResourceFor(resource unversioned.GroupVersionResourc
|
||||
return resources[0], nil
|
||||
}
|
||||
|
||||
return unversioned.GroupVersionResource{}, fmt.Errorf("%v is ambiguous, got: %v", resource, resources)
|
||||
return unversioned.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources}
|
||||
}
|
||||
|
||||
func (m *DefaultRESTMapper) KindsFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
|
||||
@ -309,7 +309,7 @@ func (m *DefaultRESTMapper) KindsFor(input unversioned.GroupVersionResource) ([]
|
||||
}
|
||||
|
||||
if len(ret) == 0 {
|
||||
return nil, fmt.Errorf("no kind %v has been defined; known resources: %v", resource, m.pluralToSingular)
|
||||
return nil, &NoResourceMatchError{PartialResource: input}
|
||||
}
|
||||
|
||||
sort.Sort(kindByPreferredGroupVersion{ret, m.defaultGroupVersions})
|
||||
@ -340,7 +340,7 @@ func (m *DefaultRESTMapper) KindFor(resource unversioned.GroupVersionResource) (
|
||||
return oneKindPerGroup[0], nil
|
||||
}
|
||||
|
||||
return unversioned.GroupVersionKind{}, fmt.Errorf("%v is ambiguous, got: %v", resource, kinds)
|
||||
return unversioned.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds}
|
||||
}
|
||||
|
||||
type kindByPreferredGroupVersion struct {
|
||||
@ -519,106 +519,3 @@ func (m *DefaultRESTMapper) ResourceIsValid(resource unversioned.GroupVersionRes
|
||||
_, err := m.KindFor(resource)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// MultiRESTMapper is a wrapper for multiple RESTMappers.
|
||||
type MultiRESTMapper []RESTMapper
|
||||
|
||||
func (m MultiRESTMapper) String() string {
|
||||
nested := []string{}
|
||||
for _, t := range m {
|
||||
currString := fmt.Sprintf("%v", t)
|
||||
splitStrings := strings.Split(currString, "\n")
|
||||
nested = append(nested, strings.Join(splitStrings, "\n\t"))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("MultiRESTMapper{\n\t%s\n}", strings.Join(nested, "\n\t"))
|
||||
}
|
||||
|
||||
// ResourceSingularizer converts a REST resource name from plural to singular (e.g., from pods to pod)
|
||||
// This implementation supports multiple REST schemas and return the first match.
|
||||
func (m MultiRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
|
||||
for _, t := range m {
|
||||
singular, err = t.ResourceSingularizer(resource)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m MultiRESTMapper) ResourcesFor(resource unversioned.GroupVersionResource) (gvk []unversioned.GroupVersionResource, err error) {
|
||||
for _, t := range m {
|
||||
gvk, err = t.ResourcesFor(resource)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KindsFor provides the Kind mappings for the REST resources. This implementation supports multiple REST schemas and returns
|
||||
// the first match.
|
||||
func (m MultiRESTMapper) KindsFor(resource unversioned.GroupVersionResource) (gvk []unversioned.GroupVersionKind, err error) {
|
||||
for _, t := range m {
|
||||
gvk, err = t.KindsFor(resource)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m MultiRESTMapper) ResourceFor(resource unversioned.GroupVersionResource) (gvk unversioned.GroupVersionResource, err error) {
|
||||
for _, t := range m {
|
||||
gvk, err = t.ResourceFor(resource)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KindsFor provides the Kind mapping for the REST resources. This implementation supports multiple REST schemas and returns
|
||||
// the first match.
|
||||
func (m MultiRESTMapper) KindFor(resource unversioned.GroupVersionResource) (gvk unversioned.GroupVersionKind, err error) {
|
||||
for _, t := range m {
|
||||
gvk, err = t.KindFor(resource)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RESTMapping provides the REST mapping for the resource based on the
|
||||
// kind and version. This implementation supports multiple REST schemas and
|
||||
// return the first match.
|
||||
func (m MultiRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
|
||||
for _, t := range m {
|
||||
mapping, err = t.RESTMapping(gk, versions...)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AliasesForResource finds the first alias response for the provided mappers.
|
||||
func (m MultiRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) {
|
||||
for _, t := range m {
|
||||
if aliases, ok = t.AliasesForResource(alias); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ResourceIsValid takes a string (either group/kind or kind) and checks if it's a valid resource
|
||||
func (m MultiRESTMapper) ResourceIsValid(resource unversioned.GroupVersionResource) bool {
|
||||
for _, t := range m {
|
||||
if t.ResourceIsValid(resource) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ func TestRESTMapperKindsFor(t *testing.T) {
|
||||
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
|
||||
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
|
||||
},
|
||||
ExpectedKindErr: "is ambiguous",
|
||||
ExpectedKindErr: " matches multiple kinds ",
|
||||
},
|
||||
|
||||
{
|
||||
@ -188,7 +188,7 @@ func TestRESTMapperKindsFor(t *testing.T) {
|
||||
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
|
||||
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
|
||||
},
|
||||
ExpectedKindErr: "is ambiguous",
|
||||
ExpectedKindErr: " matches multiple kinds ",
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
@ -260,7 +260,7 @@ func TestRESTMapperResourcesFor(t *testing.T) {
|
||||
{Group: "second-group", Version: "first-version", Resource: "my-kinds"},
|
||||
{Group: "first-group", Version: "first-version", Resource: "my-kinds"},
|
||||
},
|
||||
ExpectedResourceErr: "is ambiguous",
|
||||
ExpectedResourceErr: " matches multiple resources ",
|
||||
},
|
||||
|
||||
{
|
||||
@ -300,7 +300,7 @@ func TestRESTMapperResourcesFor(t *testing.T) {
|
||||
{Group: "first-group", Version: "first-version", Resource: "my-kinds"},
|
||||
{Group: "second-group", Version: "first-version", Resource: "my-kinds"},
|
||||
},
|
||||
ExpectedResourceErr: "is ambiguous",
|
||||
ExpectedResourceErr: " matches multiple resources ",
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
|
@ -30,6 +30,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/client/unversioned/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
@ -118,6 +119,22 @@ func checkErr(err error, handleErr func(string)) {
|
||||
handleErr(MultilineError(prefix, errs))
|
||||
}
|
||||
|
||||
if meta.IsNoResourceMatchError(err) {
|
||||
noMatch := err.(*meta.NoResourceMatchError)
|
||||
|
||||
switch {
|
||||
case len(noMatch.PartialResource.Group) > 0 && len(noMatch.PartialResource.Version) > 0:
|
||||
handleErr(fmt.Sprintf("the server doesn't have a resource type %q in group %q and version %q", noMatch.PartialResource.Resource, noMatch.PartialResource.Group, noMatch.PartialResource.Version))
|
||||
case len(noMatch.PartialResource.Group) > 0:
|
||||
handleErr(fmt.Sprintf("the server doesn't have a resource type %q in group %q", noMatch.PartialResource.Resource, noMatch.PartialResource.Group))
|
||||
case len(noMatch.PartialResource.Version) > 0:
|
||||
handleErr(fmt.Sprintf("the server doesn't have a resource type %q in version %q", noMatch.PartialResource.Resource, noMatch.PartialResource.Version))
|
||||
default:
|
||||
handleErr(fmt.Sprintf("the server doesn't have a resource type %q", noMatch.PartialResource.Resource))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// handle multiline errors
|
||||
if clientcmd.IsConfigurationInvalid(err) {
|
||||
handleErr(MultilineError("Error in configuration: ", err))
|
||||
|
@ -28,8 +28,10 @@ import (
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
)
|
||||
@ -302,6 +304,43 @@ func TestCheckInvalidErr(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckNoResourceMatchError(t *testing.T) {
|
||||
tests := []struct {
|
||||
err error
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
&meta.NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}},
|
||||
`the server doesn't have a resource type "foo"`,
|
||||
},
|
||||
{
|
||||
&meta.NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Version: "theversion", Resource: "foo"}},
|
||||
`the server doesn't have a resource type "foo" in version "theversion"`,
|
||||
},
|
||||
{
|
||||
&meta.NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Group: "thegroup", Version: "theversion", Resource: "foo"}},
|
||||
`the server doesn't have a resource type "foo" in group "thegroup" and version "theversion"`,
|
||||
},
|
||||
{
|
||||
&meta.NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Group: "thegroup", Resource: "foo"}},
|
||||
`the server doesn't have a resource type "foo" in group "thegroup"`,
|
||||
},
|
||||
}
|
||||
|
||||
var errReturned string
|
||||
errHandle := func(err string) {
|
||||
errReturned = err
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
checkErr(test.err, errHandle)
|
||||
|
||||
if errReturned != test.expected {
|
||||
t.Fatalf("Got: %s, expected: %s", errReturned, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDumpReaderToFile(t *testing.T) {
|
||||
testString := "TEST STRING"
|
||||
tempFile, err := ioutil.TempFile("", "hlpers_test_dump_")
|
||||
|
Loading…
Reference in New Issue
Block a user