multirestmapper.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. // MultiRESTMapper is a wrapper for multiple RESTMappers.
  21. type MultiRESTMapper []RESTMapper
  22. func (m MultiRESTMapper) String() string {
  23. nested := []string{}
  24. for _, t := range m {
  25. currString := fmt.Sprintf("%v", t)
  26. splitStrings := strings.Split(currString, "\n")
  27. nested = append(nested, strings.Join(splitStrings, "\n\t"))
  28. }
  29. return fmt.Sprintf("MultiRESTMapper{\n\t%s\n}", strings.Join(nested, "\n\t"))
  30. }
  31. // ResourceSingularizer converts a REST resource name from plural to singular (e.g., from pods to pod)
  32. // This implementation supports multiple REST schemas and return the first match.
  33. func (m MultiRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
  34. for _, t := range m {
  35. singular, err = t.ResourceSingularizer(resource)
  36. if err == nil {
  37. return
  38. }
  39. }
  40. return
  41. }
  42. func (m MultiRESTMapper) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
  43. allGVRs := []schema.GroupVersionResource{}
  44. for _, t := range m {
  45. gvrs, err := t.ResourcesFor(resource)
  46. // ignore "no match" errors, but any other error percolates back up
  47. if IsNoMatchError(err) {
  48. continue
  49. }
  50. if err != nil {
  51. return nil, err
  52. }
  53. // walk the existing values to de-dup
  54. for _, curr := range gvrs {
  55. found := false
  56. for _, existing := range allGVRs {
  57. if curr == existing {
  58. found = true
  59. break
  60. }
  61. }
  62. if !found {
  63. allGVRs = append(allGVRs, curr)
  64. }
  65. }
  66. }
  67. if len(allGVRs) == 0 {
  68. return nil, &NoResourceMatchError{PartialResource: resource}
  69. }
  70. return allGVRs, nil
  71. }
  72. func (m MultiRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
  73. allGVKs := []schema.GroupVersionKind{}
  74. for _, t := range m {
  75. gvks, err := t.KindsFor(resource)
  76. // ignore "no match" errors, but any other error percolates back up
  77. if IsNoMatchError(err) {
  78. continue
  79. }
  80. if err != nil {
  81. return nil, err
  82. }
  83. // walk the existing values to de-dup
  84. for _, curr := range gvks {
  85. found := false
  86. for _, existing := range allGVKs {
  87. if curr == existing {
  88. found = true
  89. break
  90. }
  91. }
  92. if !found {
  93. allGVKs = append(allGVKs, curr)
  94. }
  95. }
  96. }
  97. if len(allGVKs) == 0 {
  98. return nil, &NoResourceMatchError{PartialResource: resource}
  99. }
  100. return allGVKs, nil
  101. }
  102. func (m MultiRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
  103. resources, err := m.ResourcesFor(resource)
  104. if err != nil {
  105. return schema.GroupVersionResource{}, err
  106. }
  107. if len(resources) == 1 {
  108. return resources[0], nil
  109. }
  110. return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources}
  111. }
  112. func (m MultiRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
  113. kinds, err := m.KindsFor(resource)
  114. if err != nil {
  115. return schema.GroupVersionKind{}, err
  116. }
  117. if len(kinds) == 1 {
  118. return kinds[0], nil
  119. }
  120. return schema.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds}
  121. }
  122. // RESTMapping provides the REST mapping for the resource based on the
  123. // kind and version. This implementation supports multiple REST schemas and
  124. // return the first match.
  125. func (m MultiRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
  126. allMappings := []*RESTMapping{}
  127. errors := []error{}
  128. for _, t := range m {
  129. currMapping, err := t.RESTMapping(gk, versions...)
  130. // ignore "no match" errors, but any other error percolates back up
  131. if IsNoMatchError(err) {
  132. continue
  133. }
  134. if err != nil {
  135. errors = append(errors, err)
  136. continue
  137. }
  138. allMappings = append(allMappings, currMapping)
  139. }
  140. // if we got exactly one mapping, then use it even if other requested failed
  141. if len(allMappings) == 1 {
  142. return allMappings[0], nil
  143. }
  144. if len(allMappings) > 1 {
  145. var kinds []schema.GroupVersionKind
  146. for _, m := range allMappings {
  147. kinds = append(kinds, m.GroupVersionKind)
  148. }
  149. return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds}
  150. }
  151. if len(errors) > 0 {
  152. return nil, utilerrors.NewAggregate(errors)
  153. }
  154. return nil, &NoKindMatchError{GroupKind: gk, SearchedVersions: versions}
  155. }
  156. // RESTMappings returns all possible RESTMappings for the provided group kind, or an error
  157. // if the type is not recognized.
  158. func (m MultiRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
  159. var allMappings []*RESTMapping
  160. var errors []error
  161. for _, t := range m {
  162. currMappings, err := t.RESTMappings(gk, versions...)
  163. // ignore "no match" errors, but any other error percolates back up
  164. if IsNoMatchError(err) {
  165. continue
  166. }
  167. if err != nil {
  168. errors = append(errors, err)
  169. continue
  170. }
  171. allMappings = append(allMappings, currMappings...)
  172. }
  173. if len(errors) > 0 {
  174. return nil, utilerrors.NewAggregate(errors)
  175. }
  176. if len(allMappings) == 0 {
  177. return nil, &NoKindMatchError{GroupKind: gk, SearchedVersions: versions}
  178. }
  179. return allMappings, nil
  180. }