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