Skip to content

Commit 12a5eee

Browse files
Introduce GroupVersioner for capturing desired target version
Convert single GV and lists of GVs into an interface that can handle more complex scenarios (everything internal, nothing supported). Pass the interface down into conversion.
1 parent 9d2a5fe commit 12a5eee

File tree

24 files changed

+619
-334
lines changed

24 files changed

+619
-334
lines changed

pkg/api/meta/restmapper_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func (fakeConvertor) Convert(in, out interface{}) error {
3232
return nil
3333
}
3434

35-
func (fakeConvertor) ConvertToVersion(in runtime.Object, _ unversioned.GroupVersion) (runtime.Object, error) {
35+
func (fakeConvertor) ConvertToVersion(in runtime.Object, _ runtime.GroupVersioner) (runtime.Object, error) {
3636
return in, nil
3737
}
3838

pkg/api/serialization_test.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import (
4343
"k8s.io/kubernetes/pkg/conversion"
4444
"k8s.io/kubernetes/pkg/runtime"
4545
"k8s.io/kubernetes/pkg/runtime/serializer/streaming"
46-
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
4746
"k8s.io/kubernetes/pkg/util/diff"
4847
"k8s.io/kubernetes/pkg/util/sets"
4948
"k8s.io/kubernetes/pkg/watch"
@@ -181,10 +180,14 @@ func TestSetControllerConversion(t *testing.T) {
181180
t.Fatalf("unexpected encoding error: %v", err)
182181
}
183182

184-
decoder := api.Codecs.UniversalDecoder(*extGroup.GroupVersion(), *defaultGroup.GroupVersion())
185-
if err := versioning.EnableCrossGroupDecoding(decoder, extGroup.GroupVersion().Group, defaultGroup.GroupVersion().Group); err != nil {
186-
t.Fatalf("unexpected error while enabling cross-group decoding: %v", err)
187-
}
183+
decoder := api.Codecs.DecoderToVersion(
184+
api.Codecs.UniversalDeserializer(),
185+
runtime.NewMultiGroupVersioner(
186+
*defaultGroup.GroupVersion(),
187+
unversioned.GroupKind{Group: defaultGroup.GroupVersion().Group},
188+
unversioned.GroupKind{Group: extGroup.GroupVersion().Group},
189+
),
190+
)
188191

189192
t.Logf("rs.v1beta1.extensions -> rc._internal")
190193
if err := runtime.DecodeInto(decoder, data, rc); err != nil {

pkg/api/testapi/testapi.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func (g TestGroup) Codec() runtime.Codec {
281281
if serializer.Serializer == nil {
282282
return api.Codecs.LegacyCodec(g.externalGroupVersion)
283283
}
284-
return api.Codecs.CodecForVersions(serializer, api.Codecs.UniversalDeserializer(), []unversioned.GroupVersion{g.externalGroupVersion}, nil)
284+
return api.Codecs.CodecForVersions(serializer, api.Codecs.UniversalDeserializer(), unversioned.GroupVersions{g.externalGroupVersion}, nil)
285285
}
286286

287287
// NegotiatedSerializer returns the negotiated serializer for the server.
@@ -309,7 +309,7 @@ func (g TestGroup) StorageCodec() runtime.Codec {
309309
}
310310
ds := recognizer.NewDecoder(s, api.Codecs.UniversalDeserializer())
311311

312-
return api.Codecs.CodecForVersions(s, ds, []unversioned.GroupVersion{g.externalGroupVersion}, nil)
312+
return api.Codecs.CodecForVersions(s, ds, unversioned.GroupVersions{g.externalGroupVersion}, nil)
313313
}
314314

315315
// Converter returns the api.Scheme for the API version to test against, as set by the
@@ -393,7 +393,7 @@ func (g TestGroup) RESTMapper() meta.RESTMapper {
393393
}
394394

395395
// ExternalGroupVersions returns all external group versions allowed for the server.
396-
func ExternalGroupVersions() []unversioned.GroupVersion {
396+
func ExternalGroupVersions() unversioned.GroupVersions {
397397
versions := []unversioned.GroupVersion{}
398398
for _, g := range Groups {
399399
gv := g.GroupVersion()

pkg/api/unversioned/group_version.go

+38
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,25 @@ func (gv GroupVersion) String() string {
179179
return gv.Version
180180
}
181181

182+
// KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
183+
// if none of the options match the group. It prefers a match to group and version over just group.
184+
// TODO: Move GroupVersion to a package under pkg/runtime, since it's used by scheme.
185+
// TODO: Introduce an adapter type between GroupVersion and runtime.GroupVersioner, and use LegacyCodec(GroupVersion)
186+
// in fewer places.
187+
func (gv GroupVersion) KindForGroupVersionKinds(kinds []GroupVersionKind) (target GroupVersionKind, ok bool) {
188+
for _, gvk := range kinds {
189+
if gvk.Group == gv.Group && gvk.Version == gv.Version {
190+
return gvk, true
191+
}
192+
}
193+
for _, gvk := range kinds {
194+
if gvk.Group == gv.Group {
195+
return gv.WithKind(gvk.Kind), true
196+
}
197+
}
198+
return GroupVersionKind{}, false
199+
}
200+
182201
// ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error
183202
// if it cannot parse the string.
184203
func ParseGroupVersion(gv string) (GroupVersion, error) {
@@ -241,6 +260,25 @@ func (gv *GroupVersion) UnmarshalText(value []byte) error {
241260
return gv.unmarshal(value)
242261
}
243262

263+
// GroupVersions can be used to represent a set of desired group versions.
264+
// TODO: Move GroupVersions to a package under pkg/runtime, since it's used by scheme.
265+
// TODO: Introduce an adapter type between GroupVersions and runtime.GroupVersioner, and use LegacyCodec(GroupVersion)
266+
// in fewer places.
267+
type GroupVersions []GroupVersion
268+
269+
// KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
270+
// if none of the options match the group.
271+
func (gvs GroupVersions) KindForGroupVersionKinds(kinds []GroupVersionKind) (target GroupVersionKind, ok bool) {
272+
for _, gv := range gvs {
273+
target, ok := gv.KindForGroupVersionKinds(kinds)
274+
if !ok {
275+
continue
276+
}
277+
return target, true
278+
}
279+
return GroupVersionKind{}, false
280+
}
281+
244282
// ToAPIVersionAndKind is a convenience method for satisfying runtime.Object on types that
245283
// do not use TypeMeta.
246284
func (gvk *GroupVersionKind) ToAPIVersionAndKind() (string, string) {

pkg/apiserver/apiserver.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ type StripVersionNegotiatedSerializer struct {
320320
runtime.NegotiatedSerializer
321321
}
322322

323-
func (n StripVersionNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
323+
func (n StripVersionNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
324324
serializer, ok := encoder.(runtime.Serializer)
325325
if !ok {
326326
// The stripVersionEncoder needs both an encoder and decoder, but is called from a context that doesn't have access to the

pkg/apiserver/negotiate_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ func (n *fakeNegotiater) StreamingSerializerForMediaType(mediaType string, optio
6464
}, n.streamSerializer != nil
6565
}
6666

67-
func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
67+
func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
6868
return n.serializer
6969
}
7070

71-
func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
71+
func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
7272
return n.serializer
7373
}
7474

pkg/client/unversioned/clientcmd/api/latest/latest.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func init() {
6060
Scheme,
6161
yamlSerializer,
6262
yamlSerializer,
63-
[]unversioned.GroupVersion{{Version: Version}},
64-
[]unversioned.GroupVersion{{Version: runtime.APIVersionInternal}},
63+
unversioned.GroupVersion{Version: Version},
64+
runtime.InternalGroupVersioner,
6565
)
6666
}

pkg/conversion/converter.go

+2
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ type Meta struct {
213213
// KeyNameMapping is an optional function which may map the listed key (field name)
214214
// into a source and destination value.
215215
KeyNameMapping FieldMappingFunc
216+
// Context is an optional field that callers may use to pass info to conversion functions.
217+
Context interface{}
216218
}
217219

218220
// scope contains information about an ongoing conversion.

pkg/genericapiserver/storage_factory.go

+18-12
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"k8s.io/kubernetes/pkg/api/unversioned"
2525
"k8s.io/kubernetes/pkg/runtime"
2626
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
27-
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
2827
"k8s.io/kubernetes/pkg/storage/storagebackend"
2928
"k8s.io/kubernetes/pkg/util/sets"
3029

@@ -262,18 +261,25 @@ func NewStorageCodec(storageMediaType string, ns runtime.StorageSerializer, stor
262261
s = runtime.NewBase64Serializer(s)
263262
}
264263

264+
encoder := ns.EncoderForVersion(
265+
s,
266+
runtime.NewMultiGroupVersioner(
267+
storageVersion,
268+
unversioned.GroupKind{Group: storageVersion.Group},
269+
unversioned.GroupKind{Group: memoryVersion.Group},
270+
),
271+
)
272+
265273
ds := recognizer.NewDecoder(s, ns.UniversalDeserializer())
266-
encoder := ns.EncoderForVersion(s, storageVersion)
267-
decoder := ns.DecoderToVersion(ds, memoryVersion)
268-
if memoryVersion.Group != storageVersion.Group {
269-
// Allow this codec to translate between groups.
270-
if err := versioning.EnableCrossGroupEncoding(encoder, memoryVersion.Group, storageVersion.Group); err != nil {
271-
return nil, fmt.Errorf("error setting up encoder from %v to %v: %v", memoryVersion, storageVersion, err)
272-
}
273-
if err := versioning.EnableCrossGroupDecoding(decoder, storageVersion.Group, memoryVersion.Group); err != nil {
274-
return nil, fmt.Errorf("error setting up decoder from %v to %v: %v", storageVersion, memoryVersion, err)
275-
}
276-
}
274+
decoder := ns.DecoderToVersion(
275+
ds,
276+
runtime.NewMultiGroupVersioner(
277+
memoryVersion,
278+
unversioned.GroupKind{Group: memoryVersion.Group},
279+
unversioned.GroupKind{Group: storageVersion.Group},
280+
),
281+
)
282+
277283
return runtime.NewCodec(encoder, decoder), nil
278284
}
279285

pkg/registry/thirdpartyresourcedata/codec.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ type thirdPartyObjectConverter struct {
4242
converter runtime.ObjectConvertor
4343
}
4444

45-
func (t *thirdPartyObjectConverter) ConvertToVersion(in runtime.Object, outVersion unversioned.GroupVersion) (out runtime.Object, err error) {
45+
func (t *thirdPartyObjectConverter) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (out runtime.Object, err error) {
4646
switch in.(type) {
4747
// This seems weird, but in this case the ThirdPartyResourceData is really just a wrapper on the raw 3rd party data.
4848
// The actual thing printed/sent to server is the actual raw third party resource data, which only has one version.
@@ -234,11 +234,11 @@ func (t *thirdPartyResourceDataCodecFactory) StreamingSerializerForMediaType(med
234234
}
235235
}
236236

237-
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
238-
return &thirdPartyResourceDataEncoder{delegate: t.delegate.EncoderForVersion(s, gv), gvk: gv.WithKind(t.kind)}
237+
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
238+
return &thirdPartyResourceDataEncoder{delegate: t.delegate.EncoderForVersion(s, gv), gvk: t.encodeGV.WithKind(t.kind)}
239239
}
240240

241-
func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
241+
func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
242242
return NewDecoder(t.delegate.DecoderToVersion(s, gv), t.kind)
243243
}
244244

@@ -517,6 +517,10 @@ func (t *thirdPartyResourceDataEncoder) Encode(obj runtime.Object, stream io.Wri
517517
listItems[ix] = json.RawMessage(buff.Bytes())
518518
}
519519

520+
if t.gvk.IsEmpty() {
521+
return fmt.Errorf("thirdPartyResourceDataEncoder was not given a target version")
522+
}
523+
520524
encMap := struct {
521525
Kind string `json:"kind,omitempty"`
522526
Items []json.RawMessage `json:"items"`

pkg/runtime/codec.go

+80
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,83 @@ func (s base64Serializer) Decode(data []byte, defaults *unversioned.GroupVersion
198198
}
199199
return s.Serializer.Decode(out[:n], defaults, into)
200200
}
201+
202+
var (
203+
// InternalGroupVersioner will always prefer the internal version for a given group version kind.
204+
InternalGroupVersioner GroupVersioner = internalGroupVersioner{}
205+
// DisabledGroupVersioner will reject all kinds passed to it.
206+
DisabledGroupVersioner GroupVersioner = disabledGroupVersioner{}
207+
)
208+
209+
type internalGroupVersioner struct{}
210+
211+
// KindForGroupVersionKinds returns an internal Kind if one is found, or converts the first provided kind to the internal version.
212+
func (internalGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
213+
for _, kind := range kinds {
214+
if kind.Version == APIVersionInternal {
215+
return kind, true
216+
}
217+
}
218+
for _, kind := range kinds {
219+
return unversioned.GroupVersionKind{Group: kind.Group, Version: APIVersionInternal, Kind: kind.Kind}, true
220+
}
221+
return unversioned.GroupVersionKind{}, false
222+
}
223+
224+
type disabledGroupVersioner struct{}
225+
226+
// KindForGroupVersionKinds returns false for any input.
227+
func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
228+
return unversioned.GroupVersionKind{}, false
229+
}
230+
231+
// GroupVersioners implements GroupVersioner and resolves to the first exact match for any kind.
232+
type GroupVersioners []GroupVersioner
233+
234+
// KindForGroupVersionKinds returns the first match of any of the group versioners, or false if no match occured.
235+
func (gvs GroupVersioners) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
236+
for _, gv := range gvs {
237+
target, ok := gv.KindForGroupVersionKinds(kinds)
238+
if !ok {
239+
continue
240+
}
241+
return target, true
242+
}
243+
return unversioned.GroupVersionKind{}, false
244+
}
245+
246+
// Assert that unversioned.GroupVersion and GroupVersions implement GroupVersioner
247+
var _ GroupVersioner = unversioned.GroupVersion{}
248+
var _ GroupVersioner = unversioned.GroupVersions{}
249+
var _ GroupVersioner = multiGroupVersioner{}
250+
251+
type multiGroupVersioner struct {
252+
target unversioned.GroupVersion
253+
acceptedGroupKinds []unversioned.GroupKind
254+
}
255+
256+
// NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds.
257+
// Kind may be empty in the provided group kind, in which case any kind will match.
258+
func NewMultiGroupVersioner(gv unversioned.GroupVersion, groupKinds ...unversioned.GroupKind) GroupVersioner {
259+
if len(groupKinds) == 0 || (len(groupKinds) == 1 && groupKinds[0].Group == gv.Group) {
260+
return gv
261+
}
262+
return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds}
263+
}
264+
265+
// KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will
266+
// use the originating kind where possible.
267+
func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
268+
for _, src := range kinds {
269+
for _, kind := range v.acceptedGroupKinds {
270+
if kind.Group != src.Group {
271+
continue
272+
}
273+
if len(kind.Kind) > 0 && kind.Kind != src.Kind {
274+
continue
275+
}
276+
return v.target.WithKind(src.Kind), true
277+
}
278+
}
279+
return unversioned.GroupVersionKind{}, false
280+
}

pkg/runtime/helper.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ var _ ObjectConvertor = unsafeObjectConvertor{}
3535

3636
// ConvertToVersion converts in to the provided outVersion without copying the input first, which
3737
// is only safe if the output object is not mutated or reused.
38-
func (c unsafeObjectConvertor) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
38+
func (c unsafeObjectConvertor) ConvertToVersion(in Object, outVersion GroupVersioner) (Object, error) {
3939
return c.Scheme.UnsafeConvertToVersion(in, outVersion)
4040
}
4141

0 commit comments

Comments
 (0)