mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 03:33:56 +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
|
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
|
// Non-codec interfaces
|
||||||
|
|
||||||
|
@ -17,8 +17,6 @@ limitations under the License.
|
|||||||
package serializer
|
package serializer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
"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.
|
// EncoderForVersion returns an encoder that does not do conversion. gv is ignored.
|
||||||
func (f DirectCodecFactory) EncoderForVersion(serializer runtime.Encoder, _ runtime.GroupVersioner) runtime.Encoder {
|
func (f DirectCodecFactory) EncoderForVersion(serializer runtime.Encoder, _ runtime.GroupVersioner) runtime.Encoder {
|
||||||
return DirectCodec{
|
return versioning.DirectEncoder{
|
||||||
runtime.NewCodec(serializer, nil),
|
Encoder: serializer,
|
||||||
f.CodecFactory.scheme,
|
ObjectTyper: f.CodecFactory.scheme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecoderToVersion returns an decoder that does not do conversion. gv is ignored.
|
// DecoderToVersion returns an decoder that does not do conversion. gv is ignored.
|
||||||
func (f DirectCodecFactory) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder {
|
func (f DirectCodecFactory) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder {
|
||||||
return DirectCodec{
|
return versioning.DirectDecoder{
|
||||||
runtime.NewCodec(nil, serializer),
|
Decoder: serializer,
|
||||||
nil,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
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 we specify a target, use generic conversion.
|
||||||
if into != nil {
|
if into != nil {
|
||||||
if into == obj {
|
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
|
// 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.
|
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
|
||||||
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
||||||
switch t := obj.(type) {
|
switch obj.(type) {
|
||||||
case *runtime.Unknown:
|
case *runtime.Unknown, *runtime.Unstructured, *runtime.UnstructuredList:
|
||||||
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())
|
|
||||||
}
|
|
||||||
return c.encoder.Encode(obj, w)
|
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 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()
|
objectKind := obj.GetObjectKind()
|
||||||
old := objectKind.GroupVersionKind()
|
old := objectKind.GroupVersionKind()
|
||||||
objectKind.SetGroupVersionKind(gvks[0])
|
objectKind.SetGroupVersionKind(gvks[0])
|
||||||
@ -170,9 +168,52 @@ func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
|
||||||
err = c.encoder.Encode(out, w)
|
err = c.encoder.Encode(out, w)
|
||||||
// restore the old GVK, in case conversion returned the same object
|
// restore the old GVK, in case conversion returned the same object
|
||||||
objectKind.SetGroupVersionKind(old)
|
objectKind.SetGroupVersionKind(old)
|
||||||
return err
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -37,6 +38,60 @@ func (d *testDecodable) GetObjectKind() unversioned.ObjectKind {
|
|||||||
func (d *testDecodable) SetGroupVersionKind(gvk unversioned.GroupVersionKind) { d.gvk = gvk }
|
func (d *testDecodable) SetGroupVersionKind(gvk unversioned.GroupVersionKind) { d.gvk = gvk }
|
||||||
func (d *testDecodable) GroupVersionKind() unversioned.GroupVersionKind { return d.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) {
|
func TestDecode(t *testing.T) {
|
||||||
gvk1 := &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}
|
gvk1 := &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}
|
||||||
decodable1 := &testDecodable{}
|
decodable1 := &testDecodable{}
|
||||||
@ -300,10 +355,15 @@ func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockTyper struct {
|
type mockTyper struct {
|
||||||
gvk *unversioned.GroupVersionKind
|
gvks []unversioned.GroupVersionKind
|
||||||
err error
|
unversioned bool
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) {
|
func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]unversioned.GroupVersionKind, bool, error) {
|
||||||
return t.gvk, false, t.err
|
return t.gvks, t.unversioned, t.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mockTyper) Recognizes(_ unversioned.GroupVersionKind) bool {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user