2 Copyright 2016 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
22 "k8s.io/apimachinery/pkg/runtime/schema"
32 // PriorityRESTMapper is a wrapper for automatically choosing a particular Resource or Kind
33 // when multiple matches are possible
34 type PriorityRESTMapper struct {
35 // Delegate is the RESTMapper to use to locate all the Kind and Resource matches
38 // ResourcePriority is a list of priority patterns to apply to matching resources.
39 // The list of all matching resources is narrowed based on the patterns until only one remains.
40 // A pattern with no matches is skipped. A pattern with more than one match uses its
41 // matches as the list to continue matching against.
42 ResourcePriority []schema.GroupVersionResource
44 // KindPriority is a list of priority patterns to apply to matching kinds.
45 // The list of all matching kinds is narrowed based on the patterns until only one remains.
46 // A pattern with no matches is skipped. A pattern with more than one match uses its
47 // matches as the list to continue matching against.
48 KindPriority []schema.GroupVersionKind
51 func (m PriorityRESTMapper) String() string {
52 return fmt.Sprintf("PriorityRESTMapper{\n\t%v\n\t%v\n\t%v\n}", m.ResourcePriority, m.KindPriority, m.Delegate)
55 // ResourceFor finds all resources, then passes them through the ResourcePriority patterns to find a single matching hit.
56 func (m PriorityRESTMapper) ResourceFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
57 originalGVRs, originalErr := m.Delegate.ResourcesFor(partiallySpecifiedResource)
58 if originalErr != nil && len(originalGVRs) == 0 {
59 return schema.GroupVersionResource{}, originalErr
61 if len(originalGVRs) == 1 {
62 return originalGVRs[0], originalErr
65 remainingGVRs := append([]schema.GroupVersionResource{}, originalGVRs...)
66 for _, pattern := range m.ResourcePriority {
67 matchedGVRs := []schema.GroupVersionResource{}
68 for _, gvr := range remainingGVRs {
69 if resourceMatches(pattern, gvr) {
70 matchedGVRs = append(matchedGVRs, gvr)
74 switch len(matchedGVRs) {
76 // if you have no matches, then nothing matched this pattern just move to the next
80 return matchedGVRs[0], originalErr
82 // more than one match, use the matched hits as the list moving to the next pattern.
83 // this way you can have a series of selection criteria
84 remainingGVRs = matchedGVRs
88 return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: partiallySpecifiedResource, MatchingResources: originalGVRs}
91 // KindFor finds all kinds, then passes them through the KindPriority patterns to find a single matching hit.
92 func (m PriorityRESTMapper) KindFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
93 originalGVKs, originalErr := m.Delegate.KindsFor(partiallySpecifiedResource)
94 if originalErr != nil && len(originalGVKs) == 0 {
95 return schema.GroupVersionKind{}, originalErr
97 if len(originalGVKs) == 1 {
98 return originalGVKs[0], originalErr
101 remainingGVKs := append([]schema.GroupVersionKind{}, originalGVKs...)
102 for _, pattern := range m.KindPriority {
103 matchedGVKs := []schema.GroupVersionKind{}
104 for _, gvr := range remainingGVKs {
105 if kindMatches(pattern, gvr) {
106 matchedGVKs = append(matchedGVKs, gvr)
110 switch len(matchedGVKs) {
112 // if you have no matches, then nothing matched this pattern just move to the next
116 return matchedGVKs[0], originalErr
118 // more than one match, use the matched hits as the list moving to the next pattern.
119 // this way you can have a series of selection criteria
120 remainingGVKs = matchedGVKs
124 return schema.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: partiallySpecifiedResource, MatchingKinds: originalGVKs}
127 func resourceMatches(pattern schema.GroupVersionResource, resource schema.GroupVersionResource) bool {
128 if pattern.Group != AnyGroup && pattern.Group != resource.Group {
131 if pattern.Version != AnyVersion && pattern.Version != resource.Version {
134 if pattern.Resource != AnyResource && pattern.Resource != resource.Resource {
141 func kindMatches(pattern schema.GroupVersionKind, kind schema.GroupVersionKind) bool {
142 if pattern.Group != AnyGroup && pattern.Group != kind.Group {
145 if pattern.Version != AnyVersion && pattern.Version != kind.Version {
148 if pattern.Kind != AnyKind && pattern.Kind != kind.Kind {
155 func (m PriorityRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
156 mappings, originalErr := m.Delegate.RESTMappings(gk, versions...)
157 if originalErr != nil && len(mappings) == 0 {
158 return nil, originalErr
161 // any versions the user provides take priority
162 priorities := m.KindPriority
163 if len(versions) > 0 {
164 priorities = make([]schema.GroupVersionKind, 0, len(m.KindPriority)+len(versions))
165 for _, version := range versions {
166 gv := schema.GroupVersion{
170 priorities = append(priorities, gv.WithKind(AnyKind))
172 priorities = append(priorities, m.KindPriority...)
175 remaining := append([]*RESTMapping{}, mappings...)
176 for _, pattern := range priorities {
177 var matching []*RESTMapping
178 for _, m := range remaining {
179 if kindMatches(pattern, m.GroupVersionKind) {
180 matching = append(matching, m)
184 switch len(matching) {
186 // if you have no matches, then nothing matched this pattern just move to the next
190 return matching[0], originalErr
192 // more than one match, use the matched hits as the list moving to the next pattern.
193 // this way you can have a series of selection criteria
197 if len(remaining) == 1 {
198 return remaining[0], originalErr
201 var kinds []schema.GroupVersionKind
202 for _, m := range mappings {
203 kinds = append(kinds, m.GroupVersionKind)
205 return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds}
208 func (m PriorityRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
209 return m.Delegate.RESTMappings(gk, versions...)
212 func (m PriorityRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
213 return m.Delegate.ResourceSingularizer(resource)
216 func (m PriorityRESTMapper) ResourcesFor(partiallySpecifiedResource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
217 return m.Delegate.ResourcesFor(partiallySpecifiedResource)
220 func (m PriorityRESTMapper) KindsFor(partiallySpecifiedResource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
221 return m.Delegate.KindsFor(partiallySpecifiedResource)