mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 10:19:50 +00:00
Allow objects to serialize their nested objects
Introduce an optional interface for callers to encode themselves.
This commit is contained in:
parent
12a5eeea17
commit
ce57455de6
@ -154,6 +154,18 @@ type StorageSerializer interface {
|
||||
DecoderToVersion(serializer Decoder, gv GroupVersioner) Decoder
|
||||
}
|
||||
|
||||
// NestedObjectEncoder is an optional interface that objects may implement to be given
|
||||
// an opportunity to encode any nested Objects / RawExtensions during serialization.
|
||||
type NestedObjectEncoder interface {
|
||||
EncodeNestedObjects(e Encoder) error
|
||||
}
|
||||
|
||||
// NestedObjectDecoder is an optional interface that objects may implement to be given
|
||||
// an opportunity to decode any nested Objects / RawExtensions during serialization.
|
||||
type NestedObjectDecoder interface {
|
||||
DecodeNestedObjects(d Decoder) error
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Non-codec interfaces
|
||||
|
||||
|
@ -17,8 +17,6 @@ limitations under the License.
|
||||
package serializer
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
||||
@ -336,47 +334,15 @@ type DirectCodecFactory struct {
|
||||
|
||||
// EncoderForVersion returns an encoder that does not do conversion. gv is ignored.
|
||||
func (f DirectCodecFactory) EncoderForVersion(serializer runtime.Encoder, _ runtime.GroupVersioner) runtime.Encoder {
|
||||
return DirectCodec{
|
||||
runtime.NewCodec(serializer, nil),
|
||||
f.CodecFactory.scheme,
|
||||
return versioning.DirectEncoder{
|
||||
Encoder: serializer,
|
||||
ObjectTyper: f.CodecFactory.scheme,
|
||||
}
|
||||
}
|
||||
|
||||
// DecoderToVersion returns an decoder that does not do conversion. gv is ignored.
|
||||
func (f DirectCodecFactory) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder {
|
||||
return DirectCodec{
|
||||
runtime.NewCodec(nil, serializer),
|
||||
nil,
|
||||
return versioning.DirectDecoder{
|
||||
Decoder: serializer,
|
||||
}
|
||||
}
|
||||
|
||||
// DirectCodec is a codec that does not do conversion. It sets the gvk during serialization, and removes the gvk during deserialization.
|
||||
type DirectCodec struct {
|
||||
runtime.Serializer
|
||||
runtime.ObjectTyper
|
||||
}
|
||||
|
||||
// EncodeToStream does not do conversion. It sets the gvk during serialization. overrides are ignored.
|
||||
func (c DirectCodec) Encode(obj runtime.Object, stream io.Writer) error {
|
||||
gvks, _, err := c.ObjectTyper.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kind := obj.GetObjectKind()
|
||||
oldGVK := kind.GroupVersionKind()
|
||||
kind.SetGroupVersionKind(gvks[0])
|
||||
err = c.Serializer.Encode(obj, stream)
|
||||
kind.SetGroupVersionKind(oldGVK)
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode does not do conversion. It removes the gvk during deserialization.
|
||||
func (c DirectCodec) Decode(data []byte, defaults *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
||||
obj, gvk, err := c.Serializer.Decode(data, defaults, into)
|
||||
if obj != nil {
|
||||
kind := obj.GetObjectKind()
|
||||
// clearing the gvk is just a convention of a codec
|
||||
kind.SetGroupVersionKind(unversioned.GroupVersionKind{})
|
||||
}
|
||||
return obj, gvk, err
|
||||
}
|
||||
|
@ -88,6 +88,12 @@ func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, in
|
||||
return nil, gvk, err
|
||||
}
|
||||
|
||||
if d, ok := obj.(runtime.NestedObjectDecoder); ok {
|
||||
if err := d.DecodeNestedObjects(DirectDecoder{c.decoder}); err != nil {
|
||||
return nil, gvk, err
|
||||
}
|
||||
}
|
||||
|
||||
// if we specify a target, use generic conversion.
|
||||
if into != nil {
|
||||
if into == obj {
|
||||
@ -131,21 +137,8 @@ func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, in
|
||||
// Encode ensures the provided object is output in the appropriate group and version, invoking
|
||||
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
|
||||
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
||||
switch t := obj.(type) {
|
||||
case *runtime.Unknown:
|
||||
if gv, ok := runtime.PreferredGroupVersion(c.encodeVersion); ok {
|
||||
t.APIVersion = gv.String()
|
||||
}
|
||||
return c.encoder.Encode(obj, w)
|
||||
case *runtime.Unstructured:
|
||||
if gv, ok := runtime.PreferredGroupVersion(c.encodeVersion); ok {
|
||||
t.SetAPIVersion(gv.String())
|
||||
}
|
||||
return c.encoder.Encode(obj, w)
|
||||
case *runtime.UnstructuredList:
|
||||
if gv, ok := runtime.PreferredGroupVersion(c.encodeVersion); ok {
|
||||
t.SetAPIVersion(gv.String())
|
||||
}
|
||||
switch obj.(type) {
|
||||
case *runtime.Unknown, *runtime.Unstructured, *runtime.UnstructuredList:
|
||||
return c.encoder.Encode(obj, w)
|
||||
}
|
||||
|
||||
@ -155,6 +148,11 @@ func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
||||
}
|
||||
|
||||
if c.encodeVersion == nil || isUnversioned {
|
||||
if e, ok := obj.(runtime.NestedObjectEncoder); ok {
|
||||
if err := e.EncodeNestedObjects(DirectEncoder{Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
objectKind := obj.GetObjectKind()
|
||||
old := objectKind.GroupVersionKind()
|
||||
objectKind.SetGroupVersionKind(gvks[0])
|
||||
@ -170,9 +168,52 @@ func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e, ok := out.(runtime.NestedObjectEncoder); ok {
|
||||
if err := e.EncodeNestedObjects(DirectEncoder{Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
|
||||
err = c.encoder.Encode(out, w)
|
||||
// restore the old GVK, in case conversion returned the same object
|
||||
objectKind.SetGroupVersionKind(old)
|
||||
return err
|
||||
}
|
||||
|
||||
// DirectEncoder serializes an object and ensures the GVK is set.
|
||||
type DirectEncoder struct {
|
||||
runtime.Encoder
|
||||
runtime.ObjectTyper
|
||||
}
|
||||
|
||||
// Encode does not do conversion. It sets the gvk during serialization.
|
||||
func (e DirectEncoder) Encode(obj runtime.Object, stream io.Writer) error {
|
||||
gvks, _, err := e.ObjectTyper.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kind := obj.GetObjectKind()
|
||||
oldGVK := kind.GroupVersionKind()
|
||||
kind.SetGroupVersionKind(gvks[0])
|
||||
err = e.Encoder.Encode(obj, stream)
|
||||
kind.SetGroupVersionKind(oldGVK)
|
||||
return err
|
||||
}
|
||||
|
||||
// DirectDecoder clears the group version kind of a deserialized object.
|
||||
type DirectDecoder struct {
|
||||
runtime.Decoder
|
||||
}
|
||||
|
||||
// Decode does not do conversion. It removes the gvk during deserialization.
|
||||
func (d DirectDecoder) Decode(data []byte, defaults *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
||||
obj, gvk, err := d.Decoder.Decode(data, defaults, into)
|
||||
if obj != nil {
|
||||
kind := obj.GetObjectKind()
|
||||
// clearing the gvk is just a convention of a codec
|
||||
kind.SetGroupVersionKind(unversioned.GroupVersionKind{})
|
||||
}
|
||||
return obj, gvk, err
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package versioning
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@ -37,6 +38,60 @@ func (d *testDecodable) GetObjectKind() unversioned.ObjectKind {
|
||||
func (d *testDecodable) SetGroupVersionKind(gvk unversioned.GroupVersionKind) { d.gvk = gvk }
|
||||
func (d *testDecodable) GroupVersionKind() unversioned.GroupVersionKind { return d.gvk }
|
||||
|
||||
type testNestedDecodable struct {
|
||||
Other string
|
||||
Value int `json:"value"`
|
||||
|
||||
gvk unversioned.GroupVersionKind
|
||||
nestedCalled bool
|
||||
nestedErr error
|
||||
}
|
||||
|
||||
func (d *testNestedDecodable) GetObjectKind() unversioned.ObjectKind { return d }
|
||||
func (d *testNestedDecodable) SetGroupVersionKind(gvk unversioned.GroupVersionKind) { d.gvk = gvk }
|
||||
func (d *testNestedDecodable) GroupVersionKind() unversioned.GroupVersionKind { return d.gvk }
|
||||
|
||||
func (d *testNestedDecodable) EncodeNestedObjects(e runtime.Encoder) error {
|
||||
d.nestedCalled = true
|
||||
return d.nestedErr
|
||||
}
|
||||
|
||||
func (d *testNestedDecodable) DecodeNestedObjects(_ runtime.Decoder) error {
|
||||
d.nestedCalled = true
|
||||
return d.nestedErr
|
||||
}
|
||||
|
||||
func TestNestedDecode(t *testing.T) {
|
||||
n := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode")}
|
||||
decoder := &mockSerializer{obj: n}
|
||||
codec := NewCodec(nil, decoder, nil, nil, nil, nil, nil, nil)
|
||||
if _, _, err := codec.Decode([]byte(`{}`), nil, n); err != n.nestedErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !n.nestedCalled {
|
||||
t.Errorf("did not invoke nested decoder")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNestedEncode(t *testing.T) {
|
||||
n := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode")}
|
||||
n2 := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode 2")}
|
||||
encoder := &mockSerializer{obj: n}
|
||||
codec := NewCodec(
|
||||
encoder, nil,
|
||||
&checkConvertor{obj: n2, groupVersion: unversioned.GroupVersion{Group: "other"}},
|
||||
nil, nil,
|
||||
&mockTyper{gvks: []unversioned.GroupVersionKind{{Kind: "test"}}},
|
||||
unversioned.GroupVersion{Group: "other"}, nil,
|
||||
)
|
||||
if err := codec.Encode(n, ioutil.Discard); err != n2.nestedErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if n.nestedCalled || !n2.nestedCalled {
|
||||
t.Errorf("did not invoke correct nested decoder")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
gvk1 := &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}
|
||||
decodable1 := &testDecodable{}
|
||||
@ -300,10 +355,15 @@ func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, er
|
||||
}
|
||||
|
||||
type mockTyper struct {
|
||||
gvk *unversioned.GroupVersionKind
|
||||
err error
|
||||
gvks []unversioned.GroupVersionKind
|
||||
unversioned bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) {
|
||||
return t.gvk, false, t.err
|
||||
func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]unversioned.GroupVersionKind, bool, error) {
|
||||
return t.gvks, t.unversioned, t.err
|
||||
}
|
||||
|
||||
func (t *mockTyper) Recognizes(_ unversioned.GroupVersionKind) bool {
|
||||
return true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user