2014-10-29 16:33:46 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
2014-10-29 16:33:46 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2015-03-22 21:43:00 +00:00
|
|
|
// TODO: move everything in this file to pkg/api/rest
|
2014-10-29 16:33:46 +00:00
|
|
|
package meta
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-12-07 13:12:24 +00:00
|
|
|
"sort"
|
2014-10-29 16:33:46 +00:00
|
|
|
"strings"
|
2015-11-13 13:13:55 +00:00
|
|
|
|
|
|
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
2015-12-07 13:12:24 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/sets"
|
2014-10-29 16:33:46 +00:00
|
|
|
)
|
|
|
|
|
2015-01-29 22:46:54 +00:00
|
|
|
// Implements RESTScope interface
|
|
|
|
type restScope struct {
|
|
|
|
name RESTScopeName
|
|
|
|
paramName string
|
2015-06-18 20:20:28 +00:00
|
|
|
argumentName string
|
2015-01-29 22:46:54 +00:00
|
|
|
paramDescription string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *restScope) Name() RESTScopeName {
|
|
|
|
return r.name
|
|
|
|
}
|
|
|
|
func (r *restScope) ParamName() string {
|
|
|
|
return r.paramName
|
|
|
|
}
|
2015-06-18 20:20:28 +00:00
|
|
|
func (r *restScope) ArgumentName() string {
|
|
|
|
return r.argumentName
|
2015-01-29 22:46:54 +00:00
|
|
|
}
|
|
|
|
func (r *restScope) ParamDescription() string {
|
|
|
|
return r.paramDescription
|
|
|
|
}
|
|
|
|
|
|
|
|
var RESTScopeNamespace = &restScope{
|
|
|
|
name: RESTScopeNameNamespace,
|
2015-01-19 21:50:00 +00:00
|
|
|
paramName: "namespaces",
|
2015-06-18 20:20:28 +00:00
|
|
|
argumentName: "namespace",
|
2015-01-29 22:46:54 +00:00
|
|
|
paramDescription: "object name and auth scope, such as for teams and projects",
|
|
|
|
}
|
|
|
|
|
|
|
|
var RESTScopeRoot = &restScope{
|
|
|
|
name: RESTScopeNameRoot,
|
|
|
|
}
|
|
|
|
|
2014-11-13 12:01:25 +00:00
|
|
|
// DefaultRESTMapper exposes mappings between the types defined in a
|
2014-10-29 16:33:46 +00:00
|
|
|
// runtime.Scheme. It assumes that all types defined the provided scheme
|
|
|
|
// can be mapped with the provided MetadataAccessor and Codec interfaces.
|
|
|
|
//
|
|
|
|
// The resource name of a Kind is defined as the lowercase,
|
2015-07-07 23:52:38 +00:00
|
|
|
// English-plural version of the Kind string.
|
2014-10-29 16:33:46 +00:00
|
|
|
// When converting from resource to Kind, the singular version of the
|
|
|
|
// resource name is also accepted for convenience.
|
|
|
|
//
|
|
|
|
// TODO: Only accept plural for some operations for increased control?
|
|
|
|
// (`get pod bar` vs `get pods bar`)
|
2015-11-13 13:13:55 +00:00
|
|
|
// TODO these maps should be keyed based on GroupVersionKinds
|
2014-10-29 16:33:46 +00:00
|
|
|
type DefaultRESTMapper struct {
|
2015-11-16 15:53:05 +00:00
|
|
|
defaultGroupVersions []unversioned.GroupVersion
|
2015-11-16 15:36:41 +00:00
|
|
|
|
2015-12-07 13:12:24 +00:00
|
|
|
resourceToKind map[unversioned.GroupVersionResource]unversioned.GroupVersionKind
|
|
|
|
kindToPluralResource map[unversioned.GroupVersionKind]unversioned.GroupVersionResource
|
2015-11-16 15:36:41 +00:00
|
|
|
kindToScope map[unversioned.GroupVersionKind]RESTScope
|
2015-12-07 13:12:24 +00:00
|
|
|
singularToPlural map[unversioned.GroupVersionResource]unversioned.GroupVersionResource
|
|
|
|
pluralToSingular map[unversioned.GroupVersionResource]unversioned.GroupVersionResource
|
2015-11-16 15:36:41 +00:00
|
|
|
|
2014-10-29 16:33:46 +00:00
|
|
|
interfacesFunc VersionInterfacesFunc
|
|
|
|
}
|
|
|
|
|
2015-11-16 20:14:27 +00:00
|
|
|
var _ RESTMapper = &DefaultRESTMapper{}
|
|
|
|
|
2014-11-02 14:31:29 +00:00
|
|
|
// VersionInterfacesFunc returns the appropriate codec, typer, and metadata accessor for a
|
2015-07-29 23:15:24 +00:00
|
|
|
// given api version, or an error if no such api version exists.
|
2015-12-08 18:27:26 +00:00
|
|
|
type VersionInterfacesFunc func(version unversioned.GroupVersion) (*VersionInterfaces, error)
|
2014-10-29 16:33:46 +00:00
|
|
|
|
|
|
|
// NewDefaultRESTMapper initializes a mapping between Kind and APIVersion
|
|
|
|
// to a resource name and back based on the objects in a runtime.Scheme
|
2015-08-10 20:08:34 +00:00
|
|
|
// 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),
|
2014-10-29 16:33:46 +00:00
|
|
|
// and a function that retrieves the correct codec and metadata for a given version.
|
2015-11-16 15:53:05 +00:00
|
|
|
func NewDefaultRESTMapper(defaultGroupVersions []unversioned.GroupVersion, f VersionInterfacesFunc) *DefaultRESTMapper {
|
2015-12-07 13:12:24 +00:00
|
|
|
resourceToKind := make(map[unversioned.GroupVersionResource]unversioned.GroupVersionKind)
|
|
|
|
kindToPluralResource := make(map[unversioned.GroupVersionKind]unversioned.GroupVersionResource)
|
2015-11-16 15:36:41 +00:00
|
|
|
kindToScope := make(map[unversioned.GroupVersionKind]RESTScope)
|
2015-12-07 13:12:24 +00:00
|
|
|
singularToPlural := make(map[unversioned.GroupVersionResource]unversioned.GroupVersionResource)
|
|
|
|
pluralToSingular := make(map[unversioned.GroupVersionResource]unversioned.GroupVersionResource)
|
2014-10-29 16:33:46 +00:00
|
|
|
// TODO: verify name mappings work correctly when versions differ
|
|
|
|
|
|
|
|
return &DefaultRESTMapper{
|
2015-11-16 15:36:41 +00:00
|
|
|
resourceToKind: resourceToKind,
|
|
|
|
kindToPluralResource: kindToPluralResource,
|
|
|
|
kindToScope: kindToScope,
|
2015-11-16 15:53:05 +00:00
|
|
|
defaultGroupVersions: defaultGroupVersions,
|
2015-11-16 15:36:41 +00:00
|
|
|
singularToPlural: singularToPlural,
|
|
|
|
pluralToSingular: pluralToSingular,
|
|
|
|
interfacesFunc: f,
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-07 13:12:24 +00:00
|
|
|
func (m *DefaultRESTMapper) Add(kind unversioned.GroupVersionKind, scope RESTScope, mixedCase bool) {
|
|
|
|
plural, singular := KindToResource(kind, mixedCase)
|
|
|
|
lowerPlural := plural.GroupVersion().WithResource(strings.ToLower(plural.Resource))
|
|
|
|
lowerSingular := singular.GroupVersion().WithResource(strings.ToLower(singular.Resource))
|
|
|
|
|
2015-11-16 15:36:41 +00:00
|
|
|
m.singularToPlural[singular] = plural
|
|
|
|
m.pluralToSingular[plural] = singular
|
2015-12-07 13:12:24 +00:00
|
|
|
m.singularToPlural[lowerSingular] = lowerPlural
|
|
|
|
m.pluralToSingular[lowerPlural] = lowerSingular
|
|
|
|
|
|
|
|
if _, mixedCaseExists := m.resourceToKind[plural]; !mixedCaseExists {
|
|
|
|
m.resourceToKind[plural] = kind
|
|
|
|
m.resourceToKind[singular] = kind
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, lowerCaseExists := m.resourceToKind[lowerPlural]; !lowerCaseExists && (lowerPlural != plural) {
|
|
|
|
m.resourceToKind[lowerPlural] = kind
|
|
|
|
m.resourceToKind[lowerSingular] = kind
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
2015-12-07 13:12:24 +00:00
|
|
|
|
|
|
|
m.kindToPluralResource[kind] = plural
|
|
|
|
m.kindToScope[kind] = scope
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
|
|
|
|
2015-08-18 07:35:35 +00:00
|
|
|
// KindToResource converts Kind to a resource name.
|
2015-12-07 13:12:24 +00:00
|
|
|
func KindToResource(kind unversioned.GroupVersionKind, mixedCase bool) (plural, singular unversioned.GroupVersionResource) {
|
|
|
|
kindName := kind.Kind
|
|
|
|
if len(kindName) == 0 {
|
2015-01-29 22:46:54 +00:00
|
|
|
return
|
|
|
|
}
|
2014-10-29 16:33:46 +00:00
|
|
|
if mixedCase {
|
|
|
|
// Legacy support for mixed case names
|
2015-12-07 13:12:24 +00:00
|
|
|
singular = kind.GroupVersion().WithResource(strings.ToLower(kindName[:1]) + kindName[1:])
|
2014-10-29 16:33:46 +00:00
|
|
|
} else {
|
2015-12-07 13:12:24 +00:00
|
|
|
singular = kind.GroupVersion().WithResource(strings.ToLower(kindName))
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
2015-12-07 13:12:24 +00:00
|
|
|
|
|
|
|
singularName := singular.Resource
|
|
|
|
if strings.HasSuffix(singularName, "endpoints") {
|
2015-10-24 00:17:55 +00:00
|
|
|
plural = singular
|
2015-04-15 19:23:02 +00:00
|
|
|
} else {
|
2015-12-07 13:12:24 +00:00
|
|
|
switch string(singularName[len(singularName)-1]) {
|
2015-04-15 19:23:02 +00:00
|
|
|
case "s":
|
2015-12-07 13:12:24 +00:00
|
|
|
plural = kind.GroupVersion().WithResource(singularName + "es")
|
2015-04-15 19:23:02 +00:00
|
|
|
case "y":
|
2015-12-07 13:12:24 +00:00
|
|
|
plural = kind.GroupVersion().WithResource(strings.TrimSuffix(singularName, "y") + "ies")
|
2015-04-15 19:23:02 +00:00
|
|
|
default:
|
2015-12-07 13:12:24 +00:00
|
|
|
plural = kind.GroupVersion().WithResource(singularName + "s")
|
2015-04-15 19:23:02 +00:00
|
|
|
}
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-07-01 22:47:43 +00:00
|
|
|
// ResourceSingularizer implements RESTMapper
|
|
|
|
// It converts a resource name from plural to singular (e.g., from pods to pod)
|
2015-12-07 13:12:24 +00:00
|
|
|
// It must have exactly one match and it must match case perfectly. This is congruent with old functionality
|
|
|
|
func (m *DefaultRESTMapper) ResourceSingularizer(resourceType string) (string, error) {
|
|
|
|
partialResource := unversioned.GroupVersionResource{Resource: resourceType}
|
|
|
|
resource, err := m.ResourceFor(partialResource)
|
|
|
|
if err != nil {
|
|
|
|
return resourceType, err
|
|
|
|
}
|
|
|
|
|
2015-11-16 15:36:41 +00:00
|
|
|
singular, ok := m.pluralToSingular[resource]
|
2015-07-01 22:47:43 +00:00
|
|
|
if !ok {
|
2015-12-07 13:12:24 +00:00
|
|
|
return resourceType, fmt.Errorf("no singular of resource %v has been defined", resource)
|
2015-07-01 22:47:43 +00:00
|
|
|
}
|
2015-12-07 13:12:24 +00:00
|
|
|
return singular.Resource, nil
|
2015-07-01 22:47:43 +00:00
|
|
|
}
|
|
|
|
|
2015-12-07 13:12:24 +00:00
|
|
|
func (m *DefaultRESTMapper) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
|
|
|
|
hasResource := len(resource.Resource) > 0
|
|
|
|
hasGroup := len(resource.Group) > 0
|
|
|
|
hasVersion := len(resource.Version) > 0
|
|
|
|
|
|
|
|
if !hasResource {
|
|
|
|
return nil, fmt.Errorf("a resource must be present, got: %v", resource)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret := []unversioned.GroupVersionResource{}
|
|
|
|
switch {
|
|
|
|
// fully qualified. Find the exact match
|
|
|
|
case hasGroup && hasVersion:
|
|
|
|
for plural, singular := range m.pluralToSingular {
|
|
|
|
if singular == resource {
|
|
|
|
ret = append(ret, plural)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if plural == resource {
|
|
|
|
ret = append(ret, plural)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case hasGroup:
|
|
|
|
requestedGroupResource := resource.GroupResource()
|
|
|
|
for currResource := range m.pluralToSingular {
|
|
|
|
if currResource.GroupResource() == requestedGroupResource {
|
|
|
|
ret = append(ret, currResource)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case hasVersion:
|
|
|
|
for currResource := range m.pluralToSingular {
|
|
|
|
if currResource.Version == resource.Version && currResource.Resource == resource.Resource {
|
|
|
|
ret = append(ret, currResource)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
for currResource := range m.pluralToSingular {
|
|
|
|
if currResource.Resource == resource.Resource {
|
|
|
|
ret = append(ret, currResource)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ret) == 0 {
|
|
|
|
return nil, fmt.Errorf("no resource %v has been defined; known resources: %v", resource, m.pluralToSingular)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(resourceByPreferredGroupVersion{ret, m.defaultGroupVersions})
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *DefaultRESTMapper) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
|
|
|
|
resources, err := m.ResourcesFor(resource)
|
|
|
|
if err != nil {
|
|
|
|
return unversioned.GroupVersionResource{}, err
|
|
|
|
}
|
|
|
|
if len(resources) == 1 {
|
|
|
|
return resources[0], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return unversioned.GroupVersionResource{}, fmt.Errorf("%v is ambiguous, got: %v", resource, resources)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *DefaultRESTMapper) KindsFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
|
|
|
|
resource := input.GroupVersion().WithResource(strings.ToLower(input.Resource))
|
|
|
|
|
|
|
|
hasResource := len(resource.Resource) > 0
|
|
|
|
hasGroup := len(resource.Group) > 0
|
|
|
|
hasVersion := len(resource.Version) > 0
|
|
|
|
|
|
|
|
if !hasResource {
|
|
|
|
return nil, fmt.Errorf("a resource must be present, got: %v", resource)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret := []unversioned.GroupVersionKind{}
|
|
|
|
switch {
|
|
|
|
// fully qualified. Find the exact match
|
|
|
|
case hasGroup && hasVersion:
|
|
|
|
kind, exists := m.resourceToKind[resource]
|
|
|
|
if exists {
|
|
|
|
ret = append(ret, kind)
|
|
|
|
}
|
|
|
|
|
|
|
|
case hasGroup:
|
|
|
|
requestedGroupResource := resource.GroupResource()
|
|
|
|
for currResource, currKind := range m.resourceToKind {
|
|
|
|
if currResource.GroupResource() == requestedGroupResource {
|
|
|
|
ret = append(ret, currKind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case hasVersion:
|
|
|
|
for currResource, currKind := range m.resourceToKind {
|
|
|
|
if currResource.Version == resource.Version && currResource.Resource == resource.Resource {
|
|
|
|
ret = append(ret, currKind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
for currResource, currKind := range m.resourceToKind {
|
|
|
|
if currResource.Resource == resource.Resource {
|
|
|
|
ret = append(ret, currKind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ret) == 0 {
|
|
|
|
return nil, fmt.Errorf("no kind %v has been defined; known resources: %v", resource, m.pluralToSingular)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(kindByPreferredGroupVersion{ret, m.defaultGroupVersions})
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *DefaultRESTMapper) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
|
|
|
|
kinds, err := m.KindsFor(resource)
|
|
|
|
if err != nil {
|
|
|
|
return unversioned.GroupVersionKind{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO for each group, choose the most preferred (first) version. This keeps us consistent with code today.
|
|
|
|
// eventually, we'll need a RESTMapper that is aware of what's available server-side and deconflicts that with
|
|
|
|
// user preferences
|
|
|
|
oneKindPerGroup := []unversioned.GroupVersionKind{}
|
|
|
|
groupsAdded := sets.String{}
|
|
|
|
for _, kind := range kinds {
|
|
|
|
if groupsAdded.Has(kind.Group) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
oneKindPerGroup = append(oneKindPerGroup, kind)
|
|
|
|
groupsAdded.Insert(kind.Group)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(oneKindPerGroup) == 1 {
|
|
|
|
return oneKindPerGroup[0], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return unversioned.GroupVersionKind{}, fmt.Errorf("%v is ambiguous, got: %v", resource, kinds)
|
|
|
|
}
|
|
|
|
|
|
|
|
type kindByPreferredGroupVersion struct {
|
|
|
|
list []unversioned.GroupVersionKind
|
|
|
|
sortOrder []unversioned.GroupVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o kindByPreferredGroupVersion) Len() int { return len(o.list) }
|
|
|
|
func (o kindByPreferredGroupVersion) Swap(i, j int) { o.list[i], o.list[j] = o.list[j], o.list[i] }
|
|
|
|
func (o kindByPreferredGroupVersion) Less(i, j int) bool {
|
|
|
|
lhs := o.list[i]
|
|
|
|
rhs := o.list[j]
|
|
|
|
if lhs == rhs {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if lhs.GroupVersion() == rhs.GroupVersion() {
|
|
|
|
return lhs.Kind < rhs.Kind
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, the difference is in the GroupVersion, so we need to sort with respect to the preferred order
|
|
|
|
lhsIndex := -1
|
|
|
|
rhsIndex := -1
|
|
|
|
|
|
|
|
for i := range o.sortOrder {
|
|
|
|
if o.sortOrder[i] == lhs.GroupVersion() {
|
|
|
|
lhsIndex = i
|
|
|
|
}
|
|
|
|
if o.sortOrder[i] == rhs.GroupVersion() {
|
|
|
|
rhsIndex = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if rhsIndex == -1 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return lhsIndex < rhsIndex
|
|
|
|
}
|
|
|
|
|
|
|
|
type resourceByPreferredGroupVersion struct {
|
|
|
|
list []unversioned.GroupVersionResource
|
|
|
|
sortOrder []unversioned.GroupVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o resourceByPreferredGroupVersion) Len() int { return len(o.list) }
|
|
|
|
func (o resourceByPreferredGroupVersion) Swap(i, j int) { o.list[i], o.list[j] = o.list[j], o.list[i] }
|
|
|
|
func (o resourceByPreferredGroupVersion) Less(i, j int) bool {
|
|
|
|
lhs := o.list[i]
|
|
|
|
rhs := o.list[j]
|
|
|
|
if lhs == rhs {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if lhs.GroupVersion() == rhs.GroupVersion() {
|
|
|
|
return lhs.Resource < rhs.Resource
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, the difference is in the GroupVersion, so we need to sort with respect to the preferred order
|
|
|
|
lhsIndex := -1
|
|
|
|
rhsIndex := -1
|
|
|
|
|
|
|
|
for i := range o.sortOrder {
|
|
|
|
if o.sortOrder[i] == lhs.GroupVersion() {
|
|
|
|
lhsIndex = i
|
|
|
|
}
|
|
|
|
if o.sortOrder[i] == rhs.GroupVersion() {
|
|
|
|
rhsIndex = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if rhsIndex == -1 {
|
|
|
|
return true
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
2015-12-07 13:12:24 +00:00
|
|
|
|
|
|
|
return lhsIndex < rhsIndex
|
2015-08-10 20:08:34 +00:00
|
|
|
}
|
|
|
|
|
2014-10-29 16:33:46 +00:00
|
|
|
// RESTMapping returns a struct representing the resource path and conversion interfaces a
|
2015-11-16 21:24:59 +00:00
|
|
|
// RESTClient should use to operate on the provided group/kind in order of versions. If a version search
|
2014-12-12 19:05:27 +00:00
|
|
|
// order is not provided, the search order provided to DefaultRESTMapper will be used to resolve which
|
2015-11-16 21:24:59 +00:00
|
|
|
// version should be used to access the named group/kind.
|
|
|
|
func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error) {
|
2014-12-12 19:05:27 +00:00
|
|
|
// Pick an appropriate version
|
2015-11-16 21:24:59 +00:00
|
|
|
var gvk *unversioned.GroupVersionKind
|
|
|
|
hadVersion := false
|
|
|
|
for _, version := range versions {
|
|
|
|
if len(version) == 0 {
|
2014-12-12 19:05:27 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-11-13 13:13:55 +00:00
|
|
|
|
2015-11-16 21:24:59 +00:00
|
|
|
currGVK := gk.WithVersion(version)
|
|
|
|
hadVersion = true
|
2015-11-16 15:36:41 +00:00
|
|
|
if _, ok := m.kindToPluralResource[currGVK]; ok {
|
2015-11-16 21:24:59 +00:00
|
|
|
gvk = &currGVK
|
2014-12-12 19:05:27 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Use the default preferred versions
|
2015-11-16 21:24:59 +00:00
|
|
|
if !hadVersion && (gvk == nil) {
|
|
|
|
for _, gv := range m.defaultGroupVersions {
|
|
|
|
if gv.Group != gk.Group {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
currGVK := gk.WithVersion(gv.Version)
|
2015-11-16 15:36:41 +00:00
|
|
|
if _, ok := m.kindToPluralResource[currGVK]; ok {
|
2015-11-16 21:24:59 +00:00
|
|
|
gvk = &currGVK
|
2014-10-29 16:33:46 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2014-12-12 19:05:27 +00:00
|
|
|
}
|
2015-11-16 21:24:59 +00:00
|
|
|
if gvk == nil {
|
|
|
|
return nil, fmt.Errorf("no kind named %q is registered in versions %q", gk, versions)
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure we have a REST mapping
|
2015-11-16 21:24:59 +00:00
|
|
|
resource, ok := m.kindToPluralResource[*gvk]
|
2014-10-29 16:33:46 +00:00
|
|
|
if !ok {
|
2015-11-13 13:13:55 +00:00
|
|
|
found := []unversioned.GroupVersion{}
|
2015-11-16 15:53:05 +00:00
|
|
|
for _, gv := range m.defaultGroupVersions {
|
2015-11-16 21:24:59 +00:00
|
|
|
if _, ok := m.kindToPluralResource[*gvk]; ok {
|
2015-11-13 13:13:55 +00:00
|
|
|
found = append(found, gv)
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(found) > 0 {
|
2015-11-16 21:24:59 +00:00
|
|
|
return nil, fmt.Errorf("object with kind %q exists in versions %v, not %v", gvk.Kind, found, gvk.GroupVersion().String())
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
2015-11-16 21:24:59 +00:00
|
|
|
return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported object", gvk.GroupVersion().String(), gvk.Kind)
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
|
|
|
|
2015-01-29 16:35:06 +00:00
|
|
|
// Ensure we have a REST scope
|
2015-11-16 21:24:59 +00:00
|
|
|
scope, ok := m.kindToScope[*gvk]
|
2015-01-29 16:35:06 +00:00
|
|
|
if !ok {
|
2015-11-13 13:13:55 +00:00
|
|
|
return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", gvk.GroupVersion().String(), gvk.Kind)
|
2015-01-29 16:35:06 +00:00
|
|
|
}
|
|
|
|
|
2015-12-08 18:27:26 +00:00
|
|
|
interfaces, err := m.interfacesFunc(gvk.GroupVersion())
|
2015-07-29 23:15:24 +00:00
|
|
|
if err != nil {
|
2015-11-13 13:13:55 +00:00
|
|
|
return nil, fmt.Errorf("the provided version %q has no relevant versions", gvk.GroupVersion().String())
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
|
|
|
|
2015-04-15 19:23:02 +00:00
|
|
|
retVal := &RESTMapping{
|
2015-12-07 13:12:24 +00:00
|
|
|
Resource: resource.Resource,
|
2015-11-16 21:24:59 +00:00
|
|
|
GroupVersionKind: *gvk,
|
2015-11-13 13:13:55 +00:00
|
|
|
Scope: scope,
|
2014-11-02 14:21:25 +00:00
|
|
|
|
2014-10-29 16:33:46 +00:00
|
|
|
Codec: interfaces.Codec,
|
2014-11-02 14:31:29 +00:00
|
|
|
ObjectConvertor: interfaces.ObjectConvertor,
|
2014-10-29 16:33:46 +00:00
|
|
|
MetadataAccessor: interfaces.MetadataAccessor,
|
2015-04-15 19:23:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return retVal, nil
|
2014-10-29 16:33:46 +00:00
|
|
|
}
|
2015-04-08 15:05:41 +00:00
|
|
|
|
|
|
|
// aliasToResource is used for mapping aliases to resources
|
|
|
|
var aliasToResource = map[string][]string{}
|
|
|
|
|
|
|
|
// AddResourceAlias maps aliases to resources
|
|
|
|
func (m *DefaultRESTMapper) AddResourceAlias(alias string, resources ...string) {
|
|
|
|
if len(resources) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
aliasToResource[alias] = resources
|
|
|
|
}
|
|
|
|
|
|
|
|
// AliasesForResource returns whether a resource has an alias or not
|
|
|
|
func (m *DefaultRESTMapper) AliasesForResource(alias string) ([]string, bool) {
|
|
|
|
if res, ok := aliasToResource[alias]; ok {
|
|
|
|
return res, true
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
2015-04-16 23:21:13 +00:00
|
|
|
|
2015-12-07 13:12:24 +00:00
|
|
|
// ResourceIsValid takes a partial resource and checks if it's valid
|
|
|
|
func (m *DefaultRESTMapper) ResourceIsValid(resource unversioned.GroupVersionResource) bool {
|
2015-11-16 20:14:27 +00:00
|
|
|
_, err := m.KindFor(resource)
|
2015-09-24 01:17:09 +00:00
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
2015-04-16 23:21:13 +00:00
|
|
|
// MultiRESTMapper is a wrapper for multiple RESTMappers.
|
|
|
|
type MultiRESTMapper []RESTMapper
|
|
|
|
|
2015-07-01 22:47:43 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2015-12-07 13:12:24 +00:00
|
|
|
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
|
2015-04-16 23:21:13 +00:00
|
|
|
// the first match.
|
2015-12-07 13:12:24 +00:00
|
|
|
func (m MultiRESTMapper) KindFor(resource unversioned.GroupVersionResource) (gvk unversioned.GroupVersionKind, err error) {
|
2015-08-10 20:08:34 +00:00
|
|
|
for _, t := range m {
|
2015-11-16 20:14:27 +00:00
|
|
|
gvk, err = t.KindFor(resource)
|
2015-08-10 20:08:34 +00:00
|
|
|
if err == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-11-16 21:24:59 +00:00
|
|
|
// RESTMapping provides the REST mapping for the resource based on the
|
2015-04-16 23:21:13 +00:00
|
|
|
// kind and version. This implementation supports multiple REST schemas and
|
|
|
|
// return the first match.
|
2015-11-16 21:24:59 +00:00
|
|
|
func (m MultiRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
|
2015-04-16 23:21:13 +00:00
|
|
|
for _, t := range m {
|
2015-11-16 21:24:59 +00:00
|
|
|
mapping, err = t.RESTMapping(gk, versions...)
|
2015-04-16 23:21:13 +00:00
|
|
|
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
|
|
|
|
}
|
2015-09-24 01:17:09 +00:00
|
|
|
|
|
|
|
// ResourceIsValid takes a string (either group/kind or kind) and checks if it's a valid resource
|
2015-12-07 13:12:24 +00:00
|
|
|
func (m MultiRESTMapper) ResourceIsValid(resource unversioned.GroupVersionResource) bool {
|
2015-09-24 01:17:09 +00:00
|
|
|
for _, t := range m {
|
|
|
|
if t.ResourceIsValid(resource) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|