group_version.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. Copyright 2015 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 schema
  14. import (
  15. "fmt"
  16. "strings"
  17. )
  18. // ParseResourceArg takes the common style of string which may be either `resource.group.com` or `resource.version.group.com`
  19. // and parses it out into both possibilities. This code takes no responsibility for knowing which representation was intended
  20. // but with a knowledge of all GroupVersions, calling code can take a very good guess. If there are only two segments, then
  21. // `*GroupVersionResource` is nil.
  22. // `resource.group.com` -> `group=com, version=group, resource=resource` and `group=group.com, resource=resource`
  23. func ParseResourceArg(arg string) (*GroupVersionResource, GroupResource) {
  24. var gvr *GroupVersionResource
  25. if strings.Count(arg, ".") >= 2 {
  26. s := strings.SplitN(arg, ".", 3)
  27. gvr = &GroupVersionResource{Group: s[2], Version: s[1], Resource: s[0]}
  28. }
  29. return gvr, ParseGroupResource(arg)
  30. }
  31. // ParseKindArg takes the common style of string which may be either `Kind.group.com` or `Kind.version.group.com`
  32. // and parses it out into both possibilities. This code takes no responsibility for knowing which representation was intended
  33. // but with a knowledge of all GroupKinds, calling code can take a very good guess. If there are only two segments, then
  34. // `*GroupVersionKind` is nil.
  35. // `Kind.group.com` -> `group=com, version=group, kind=Kind` and `group=group.com, kind=Kind`
  36. func ParseKindArg(arg string) (*GroupVersionKind, GroupKind) {
  37. var gvk *GroupVersionKind
  38. if strings.Count(arg, ".") >= 2 {
  39. s := strings.SplitN(arg, ".", 3)
  40. gvk = &GroupVersionKind{Group: s[2], Version: s[1], Kind: s[0]}
  41. }
  42. return gvk, ParseGroupKind(arg)
  43. }
  44. // GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying
  45. // concepts during lookup stages without having partially valid types
  46. type GroupResource struct {
  47. Group string
  48. Resource string
  49. }
  50. func (gr GroupResource) WithVersion(version string) GroupVersionResource {
  51. return GroupVersionResource{Group: gr.Group, Version: version, Resource: gr.Resource}
  52. }
  53. func (gr GroupResource) Empty() bool {
  54. return len(gr.Group) == 0 && len(gr.Resource) == 0
  55. }
  56. func (gr GroupResource) String() string {
  57. if len(gr.Group) == 0 {
  58. return gr.Resource
  59. }
  60. return gr.Resource + "." + gr.Group
  61. }
  62. func ParseGroupKind(gk string) GroupKind {
  63. i := strings.Index(gk, ".")
  64. if i == -1 {
  65. return GroupKind{Kind: gk}
  66. }
  67. return GroupKind{Group: gk[i+1:], Kind: gk[:i]}
  68. }
  69. // ParseGroupResource turns "resource.group" string into a GroupResource struct. Empty strings are allowed
  70. // for each field.
  71. func ParseGroupResource(gr string) GroupResource {
  72. if i := strings.Index(gr, "."); i >= 0 {
  73. return GroupResource{Group: gr[i+1:], Resource: gr[:i]}
  74. }
  75. return GroupResource{Resource: gr}
  76. }
  77. // GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion
  78. // to avoid automatic coercion. It doesn't use a GroupVersion to avoid custom marshalling
  79. type GroupVersionResource struct {
  80. Group string
  81. Version string
  82. Resource string
  83. }
  84. func (gvr GroupVersionResource) Empty() bool {
  85. return len(gvr.Group) == 0 && len(gvr.Version) == 0 && len(gvr.Resource) == 0
  86. }
  87. func (gvr GroupVersionResource) GroupResource() GroupResource {
  88. return GroupResource{Group: gvr.Group, Resource: gvr.Resource}
  89. }
  90. func (gvr GroupVersionResource) GroupVersion() GroupVersion {
  91. return GroupVersion{Group: gvr.Group, Version: gvr.Version}
  92. }
  93. func (gvr GroupVersionResource) String() string {
  94. return strings.Join([]string{gvr.Group, "/", gvr.Version, ", Resource=", gvr.Resource}, "")
  95. }
  96. // GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying
  97. // concepts during lookup stages without having partially valid types
  98. type GroupKind struct {
  99. Group string
  100. Kind string
  101. }
  102. func (gk GroupKind) Empty() bool {
  103. return len(gk.Group) == 0 && len(gk.Kind) == 0
  104. }
  105. func (gk GroupKind) WithVersion(version string) GroupVersionKind {
  106. return GroupVersionKind{Group: gk.Group, Version: version, Kind: gk.Kind}
  107. }
  108. func (gk GroupKind) String() string {
  109. if len(gk.Group) == 0 {
  110. return gk.Kind
  111. }
  112. return gk.Kind + "." + gk.Group
  113. }
  114. // GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion
  115. // to avoid automatic coercion. It doesn't use a GroupVersion to avoid custom marshalling
  116. type GroupVersionKind struct {
  117. Group string
  118. Version string
  119. Kind string
  120. }
  121. // Empty returns true if group, version, and kind are empty
  122. func (gvk GroupVersionKind) Empty() bool {
  123. return len(gvk.Group) == 0 && len(gvk.Version) == 0 && len(gvk.Kind) == 0
  124. }
  125. func (gvk GroupVersionKind) GroupKind() GroupKind {
  126. return GroupKind{Group: gvk.Group, Kind: gvk.Kind}
  127. }
  128. func (gvk GroupVersionKind) GroupVersion() GroupVersion {
  129. return GroupVersion{Group: gvk.Group, Version: gvk.Version}
  130. }
  131. func (gvk GroupVersionKind) String() string {
  132. return gvk.Group + "/" + gvk.Version + ", Kind=" + gvk.Kind
  133. }
  134. // GroupVersion contains the "group" and the "version", which uniquely identifies the API.
  135. type GroupVersion struct {
  136. Group string
  137. Version string
  138. }
  139. // Empty returns true if group and version are empty
  140. func (gv GroupVersion) Empty() bool {
  141. return len(gv.Group) == 0 && len(gv.Version) == 0
  142. }
  143. // String puts "group" and "version" into a single "group/version" string. For the legacy v1
  144. // it returns "v1".
  145. func (gv GroupVersion) String() string {
  146. if len(gv.Group) > 0 {
  147. return gv.Group + "/" + gv.Version
  148. }
  149. return gv.Version
  150. }
  151. // Identifier implements runtime.GroupVersioner interface.
  152. func (gv GroupVersion) Identifier() string {
  153. return gv.String()
  154. }
  155. // KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
  156. // if none of the options match the group. It prefers a match to group and version over just group.
  157. // TODO: Move GroupVersion to a package under pkg/runtime, since it's used by scheme.
  158. // TODO: Introduce an adapter type between GroupVersion and runtime.GroupVersioner, and use LegacyCodec(GroupVersion)
  159. // in fewer places.
  160. func (gv GroupVersion) KindForGroupVersionKinds(kinds []GroupVersionKind) (target GroupVersionKind, ok bool) {
  161. for _, gvk := range kinds {
  162. if gvk.Group == gv.Group && gvk.Version == gv.Version {
  163. return gvk, true
  164. }
  165. }
  166. for _, gvk := range kinds {
  167. if gvk.Group == gv.Group {
  168. return gv.WithKind(gvk.Kind), true
  169. }
  170. }
  171. return GroupVersionKind{}, false
  172. }
  173. // ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error
  174. // if it cannot parse the string.
  175. func ParseGroupVersion(gv string) (GroupVersion, error) {
  176. // this can be the internal version for the legacy kube types
  177. // TODO once we've cleared the last uses as strings, this special case should be removed.
  178. if (len(gv) == 0) || (gv == "/") {
  179. return GroupVersion{}, nil
  180. }
  181. switch strings.Count(gv, "/") {
  182. case 0:
  183. return GroupVersion{"", gv}, nil
  184. case 1:
  185. i := strings.Index(gv, "/")
  186. return GroupVersion{gv[:i], gv[i+1:]}, nil
  187. default:
  188. return GroupVersion{}, fmt.Errorf("unexpected GroupVersion string: %v", gv)
  189. }
  190. }
  191. // WithKind creates a GroupVersionKind based on the method receiver's GroupVersion and the passed Kind.
  192. func (gv GroupVersion) WithKind(kind string) GroupVersionKind {
  193. return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
  194. }
  195. // WithResource creates a GroupVersionResource based on the method receiver's GroupVersion and the passed Resource.
  196. func (gv GroupVersion) WithResource(resource string) GroupVersionResource {
  197. return GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: resource}
  198. }
  199. // GroupVersions can be used to represent a set of desired group versions.
  200. // TODO: Move GroupVersions to a package under pkg/runtime, since it's used by scheme.
  201. // TODO: Introduce an adapter type between GroupVersions and runtime.GroupVersioner, and use LegacyCodec(GroupVersion)
  202. // in fewer places.
  203. type GroupVersions []GroupVersion
  204. // Identifier implements runtime.GroupVersioner interface.
  205. func (gvs GroupVersions) Identifier() string {
  206. groupVersions := make([]string, 0, len(gvs))
  207. for i := range gvs {
  208. groupVersions = append(groupVersions, gvs[i].String())
  209. }
  210. return fmt.Sprintf("[%s]", strings.Join(groupVersions, ","))
  211. }
  212. // KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
  213. // if none of the options match the group.
  214. func (gvs GroupVersions) KindForGroupVersionKinds(kinds []GroupVersionKind) (GroupVersionKind, bool) {
  215. var targets []GroupVersionKind
  216. for _, gv := range gvs {
  217. target, ok := gv.KindForGroupVersionKinds(kinds)
  218. if !ok {
  219. continue
  220. }
  221. targets = append(targets, target)
  222. }
  223. if len(targets) == 1 {
  224. return targets[0], true
  225. }
  226. if len(targets) > 1 {
  227. return bestMatch(kinds, targets), true
  228. }
  229. return GroupVersionKind{}, false
  230. }
  231. // bestMatch tries to pick best matching GroupVersionKind and falls back to the first
  232. // found if no exact match exists.
  233. func bestMatch(kinds []GroupVersionKind, targets []GroupVersionKind) GroupVersionKind {
  234. for _, gvk := range targets {
  235. for _, k := range kinds {
  236. if k == gvk {
  237. return k
  238. }
  239. }
  240. }
  241. return targets[0]
  242. }
  243. // ToAPIVersionAndKind is a convenience method for satisfying runtime.Object on types that
  244. // do not use TypeMeta.
  245. func (gvk GroupVersionKind) ToAPIVersionAndKind() (string, string) {
  246. if gvk.Empty() {
  247. return "", ""
  248. }
  249. return gvk.GroupVersion().String(), gvk.Kind
  250. }
  251. // FromAPIVersionAndKind returns a GVK representing the provided fields for types that
  252. // do not use TypeMeta. This method exists to support test types and legacy serializations
  253. // that have a distinct group and kind.
  254. // TODO: further reduce usage of this method.
  255. func FromAPIVersionAndKind(apiVersion, kind string) GroupVersionKind {
  256. if gv, err := ParseGroupVersion(apiVersion); err == nil {
  257. return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
  258. }
  259. return GroupVersionKind{Kind: kind}
  260. }