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