You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
7.4 KiB
222 lines
7.4 KiB
/* |
|
Copyright 2016 The Kubernetes Authors. |
|
|
|
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/apimachinery/pkg/runtime/schema" |
|
) |
|
|
|
const ( |
|
AnyGroup = "*" |
|
AnyVersion = "*" |
|
AnyResource = "*" |
|
AnyKind = "*" |
|
) |
|
|
|
// PriorityRESTMapper is a wrapper for automatically choosing a particular Resource or Kind |
|
// when multiple matches are possible |
|
type PriorityRESTMapper struct { |
|
// Delegate is the RESTMapper to use to locate all the Kind and Resource matches |
|
Delegate RESTMapper |
|
|
|
// ResourcePriority is a list of priority patterns to apply to matching resources. |
|
// The list of all matching resources is narrowed based on the patterns until only one remains. |
|
// A pattern with no matches is skipped. A pattern with more than one match uses its |
|
// matches as the list to continue matching against. |
|
ResourcePriority []schema.GroupVersionResource |
|
|
|
// KindPriority is a list of priority patterns to apply to matching kinds. |
|
// The list of all matching kinds is narrowed based on the patterns until only one remains. |
|
// A pattern with no matches is skipped. A pattern with more than one match uses its |
|
// matches as the list to continue matching against. |
|
KindPriority []schema.GroupVersionKind |
|
} |
|
|
|
func (m PriorityRESTMapper) String() string { |
|
return fmt.Sprintf("PriorityRESTMapper{\n\t%v\n\t%v\n\t%v\n}", m.ResourcePriority, m.KindPriority, m.Delegate) |
|
} |
|
|
|
// ResourceFor finds all resources, then passes them through the ResourcePriority patterns to find a single matching hit. |
|
func (m PriorityRESTMapper) ResourceFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionResource, error) { |
|
originalGVRs, originalErr := m.Delegate.ResourcesFor(partiallySpecifiedResource) |
|
if originalErr != nil && len(originalGVRs) == 0 { |
|
return schema.GroupVersionResource{}, originalErr |
|
} |
|
if len(originalGVRs) == 1 { |
|
return originalGVRs[0], originalErr |
|
} |
|
|
|
remainingGVRs := append([]schema.GroupVersionResource{}, originalGVRs...) |
|
for _, pattern := range m.ResourcePriority { |
|
matchedGVRs := []schema.GroupVersionResource{} |
|
for _, gvr := range remainingGVRs { |
|
if resourceMatches(pattern, gvr) { |
|
matchedGVRs = append(matchedGVRs, gvr) |
|
} |
|
} |
|
|
|
switch len(matchedGVRs) { |
|
case 0: |
|
// if you have no matches, then nothing matched this pattern just move to the next |
|
continue |
|
case 1: |
|
// one match, return |
|
return matchedGVRs[0], originalErr |
|
default: |
|
// more than one match, use the matched hits as the list moving to the next pattern. |
|
// this way you can have a series of selection criteria |
|
remainingGVRs = matchedGVRs |
|
} |
|
} |
|
|
|
return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: partiallySpecifiedResource, MatchingResources: originalGVRs} |
|
} |
|
|
|
// KindFor finds all kinds, then passes them through the KindPriority patterns to find a single matching hit. |
|
func (m PriorityRESTMapper) KindFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionKind, error) { |
|
originalGVKs, originalErr := m.Delegate.KindsFor(partiallySpecifiedResource) |
|
if originalErr != nil && len(originalGVKs) == 0 { |
|
return schema.GroupVersionKind{}, originalErr |
|
} |
|
if len(originalGVKs) == 1 { |
|
return originalGVKs[0], originalErr |
|
} |
|
|
|
remainingGVKs := append([]schema.GroupVersionKind{}, originalGVKs...) |
|
for _, pattern := range m.KindPriority { |
|
matchedGVKs := []schema.GroupVersionKind{} |
|
for _, gvr := range remainingGVKs { |
|
if kindMatches(pattern, gvr) { |
|
matchedGVKs = append(matchedGVKs, gvr) |
|
} |
|
} |
|
|
|
switch len(matchedGVKs) { |
|
case 0: |
|
// if you have no matches, then nothing matched this pattern just move to the next |
|
continue |
|
case 1: |
|
// one match, return |
|
return matchedGVKs[0], originalErr |
|
default: |
|
// more than one match, use the matched hits as the list moving to the next pattern. |
|
// this way you can have a series of selection criteria |
|
remainingGVKs = matchedGVKs |
|
} |
|
} |
|
|
|
return schema.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: partiallySpecifiedResource, MatchingKinds: originalGVKs} |
|
} |
|
|
|
func resourceMatches(pattern schema.GroupVersionResource, resource schema.GroupVersionResource) bool { |
|
if pattern.Group != AnyGroup && pattern.Group != resource.Group { |
|
return false |
|
} |
|
if pattern.Version != AnyVersion && pattern.Version != resource.Version { |
|
return false |
|
} |
|
if pattern.Resource != AnyResource && pattern.Resource != resource.Resource { |
|
return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
func kindMatches(pattern schema.GroupVersionKind, kind schema.GroupVersionKind) bool { |
|
if pattern.Group != AnyGroup && pattern.Group != kind.Group { |
|
return false |
|
} |
|
if pattern.Version != AnyVersion && pattern.Version != kind.Version { |
|
return false |
|
} |
|
if pattern.Kind != AnyKind && pattern.Kind != kind.Kind { |
|
return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
func (m PriorityRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) { |
|
mappings, originalErr := m.Delegate.RESTMappings(gk, versions...) |
|
if originalErr != nil && len(mappings) == 0 { |
|
return nil, originalErr |
|
} |
|
|
|
// any versions the user provides take priority |
|
priorities := m.KindPriority |
|
if len(versions) > 0 { |
|
priorities = make([]schema.GroupVersionKind, 0, len(m.KindPriority)+len(versions)) |
|
for _, version := range versions { |
|
gv := schema.GroupVersion{ |
|
Version: version, |
|
Group: gk.Group, |
|
} |
|
priorities = append(priorities, gv.WithKind(AnyKind)) |
|
} |
|
priorities = append(priorities, m.KindPriority...) |
|
} |
|
|
|
remaining := append([]*RESTMapping{}, mappings...) |
|
for _, pattern := range priorities { |
|
var matching []*RESTMapping |
|
for _, m := range remaining { |
|
if kindMatches(pattern, m.GroupVersionKind) { |
|
matching = append(matching, m) |
|
} |
|
} |
|
|
|
switch len(matching) { |
|
case 0: |
|
// if you have no matches, then nothing matched this pattern just move to the next |
|
continue |
|
case 1: |
|
// one match, return |
|
return matching[0], originalErr |
|
default: |
|
// more than one match, use the matched hits as the list moving to the next pattern. |
|
// this way you can have a series of selection criteria |
|
remaining = matching |
|
} |
|
} |
|
if len(remaining) == 1 { |
|
return remaining[0], originalErr |
|
} |
|
|
|
var kinds []schema.GroupVersionKind |
|
for _, m := range mappings { |
|
kinds = append(kinds, m.GroupVersionKind) |
|
} |
|
return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds} |
|
} |
|
|
|
func (m PriorityRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) { |
|
return m.Delegate.RESTMappings(gk, versions...) |
|
} |
|
|
|
func (m PriorityRESTMapper) ResourceSingularizer(resource string) (singular string, err error) { |
|
return m.Delegate.ResourceSingularizer(resource) |
|
} |
|
|
|
func (m PriorityRESTMapper) ResourcesFor(partiallySpecifiedResource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) { |
|
return m.Delegate.ResourcesFor(partiallySpecifiedResource) |
|
} |
|
|
|
func (m PriorityRESTMapper) KindsFor(partiallySpecifiedResource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) { |
|
return m.Delegate.KindsFor(partiallySpecifiedResource) |
|
}
|
|
|