123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- /*
- 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)
- }
|