group_version.go 11 KB

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