multirestmapper.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package meta
  14. import (
  15. "fmt"
  16. "strings"
  17. "k8s.io/apimachinery/pkg/runtime/schema"
  18. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  19. )
  20. var (
  21. _ ResettableRESTMapper = MultiRESTMapper{}
  22. )
  23. // MultiRESTMapper is a wrapper for multiple RESTMappers.
  24. type MultiRESTMapper []RESTMapper
  25. func (m MultiRESTMapper) String() string {
  26. nested := make([]string, 0, len(m))
  27. for _, t := range m {
  28. currString := fmt.Sprintf("%v", t)
  29. splitStrings := strings.Split(currString, "\n")
  30. nested = append(nested, strings.Join(splitStrings, "\n\t"))
  31. }
  32. return fmt.Sprintf("MultiRESTMapper{\n\t%s\n}", strings.Join(nested, "\n\t"))
  33. }
  34. // ResourceSingularizer converts a REST resource name from plural to singular (e.g., from pods to pod)
  35. // This implementation supports multiple REST schemas and return the first match.
  36. func (m MultiRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
  37. for _, t := range m {
  38. singular, err = t.ResourceSingularizer(resource)
  39. if err == nil {
  40. return
  41. }
  42. }
  43. return
  44. }
  45. func (m MultiRESTMapper) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
  46. allGVRs := []schema.GroupVersionResource{}
  47. for _, t := range m {
  48. gvrs, err := t.ResourcesFor(resource)
  49. // ignore "no match" errors, but any other error percolates back up
  50. if IsNoMatchError(err) {
  51. continue
  52. }
  53. if err != nil {
  54. return nil, err
  55. }
  56. // walk the existing values to de-dup
  57. for _, curr := range gvrs {
  58. found := false
  59. for _, existing := range allGVRs {
  60. if curr == existing {
  61. found = true
  62. break
  63. }
  64. }
  65. if !found {
  66. allGVRs = append(allGVRs, curr)
  67. }
  68. }
  69. }
  70. if len(allGVRs) == 0 {
  71. return nil, &NoResourceMatchError{PartialResource: resource}
  72. }
  73. return allGVRs, nil
  74. }
  75. func (m MultiRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
  76. allGVKs := []schema.GroupVersionKind{}
  77. for _, t := range m {
  78. gvks, err := t.KindsFor(resource)
  79. // ignore "no match" errors, but any other error percolates back up
  80. if IsNoMatchError(err) {
  81. continue
  82. }
  83. if err != nil {
  84. return nil, err
  85. }
  86. // walk the existing values to de-dup
  87. for _, curr := range gvks {
  88. found := false
  89. for _, existing := range allGVKs {
  90. if curr == existing {
  91. found = true
  92. break
  93. }
  94. }
  95. if !found {
  96. allGVKs = append(allGVKs, curr)
  97. }
  98. }
  99. }
  100. if len(allGVKs) == 0 {
  101. return nil, &NoResourceMatchError{PartialResource: resource}
  102. }
  103. return allGVKs, nil
  104. }
  105. func (m MultiRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
  106. resources, err := m.ResourcesFor(resource)
  107. if err != nil {
  108. return schema.GroupVersionResource{}, err
  109. }
  110. if len(resources) == 1 {
  111. return resources[0], nil
  112. }
  113. return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources}
  114. }
  115. func (m MultiRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
  116. kinds, err := m.KindsFor(resource)
  117. if err != nil {
  118. return schema.GroupVersionKind{}, err
  119. }
  120. if len(kinds) == 1 {
  121. return kinds[0], nil
  122. }
  123. return schema.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds}
  124. }
  125. // RESTMapping provides the REST mapping for the resource based on the
  126. // kind and version. This implementation supports multiple REST schemas and
  127. // return the first match.
  128. func (m MultiRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
  129. allMappings := []*RESTMapping{}
  130. errors := []error{}
  131. for _, t := range m {
  132. currMapping, err := t.RESTMapping(gk, versions...)
  133. // ignore "no match" errors, but any other error percolates back up
  134. if IsNoMatchError(err) {
  135. continue
  136. }
  137. if err != nil {
  138. errors = append(errors, err)
  139. continue
  140. }
  141. allMappings = append(allMappings, currMapping)
  142. }
  143. // if we got exactly one mapping, then use it even if other requested failed
  144. if len(allMappings) == 1 {
  145. return allMappings[0], nil
  146. }
  147. if len(allMappings) > 1 {
  148. var kinds []schema.GroupVersionKind
  149. for _, m := range allMappings {
  150. kinds = append(kinds, m.GroupVersionKind)
  151. }
  152. return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds}
  153. }
  154. if len(errors) > 0 {
  155. return nil, utilerrors.NewAggregate(errors)
  156. }
  157. return nil, &NoKindMatchError{GroupKind: gk, SearchedVersions: versions}
  158. }
  159. // RESTMappings returns all possible RESTMappings for the provided group kind, or an error
  160. // if the type is not recognized.
  161. func (m MultiRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
  162. var allMappings []*RESTMapping
  163. var errors []error
  164. for _, t := range m {
  165. currMappings, err := t.RESTMappings(gk, versions...)
  166. // ignore "no match" errors, but any other error percolates back up
  167. if IsNoMatchError(err) {
  168. continue
  169. }
  170. if err != nil {
  171. errors = append(errors, err)
  172. continue
  173. }
  174. allMappings = append(allMappings, currMappings...)
  175. }
  176. if len(errors) > 0 {
  177. return nil, utilerrors.NewAggregate(errors)
  178. }
  179. if len(allMappings) == 0 {
  180. return nil, &NoKindMatchError{GroupKind: gk, SearchedVersions: versions}
  181. }
  182. return allMappings, nil
  183. }
  184. func (m MultiRESTMapper) Reset() {
  185. for _, t := range m {
  186. MaybeResetRESTMapper(t)
  187. }
  188. }