
Add a recognizer that is capable of sniffing content type from data by asking each serializer to try to decode - this is for a "universal decoder/deserializer" which can be used by client logic. Add codec factory, which provides the core primitives for content type negotiation. Codec factories depend only on schemes, serializers, and groupversion pairs.
270 lines
8.7 KiB
Go
270 lines
8.7 KiB
Go
/*
|
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package json_test
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
|
"k8s.io/kubernetes/pkg/conversion"
|
|
"k8s.io/kubernetes/pkg/runtime"
|
|
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
|
"k8s.io/kubernetes/pkg/util"
|
|
)
|
|
|
|
type testDecodable struct {
|
|
Other string
|
|
Value int `json:"value"`
|
|
gvk *unversioned.GroupVersionKind
|
|
}
|
|
|
|
func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d }
|
|
func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk }
|
|
func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk }
|
|
|
|
func TestDecode(t *testing.T) {
|
|
testCases := []struct {
|
|
creater runtime.ObjectCreater
|
|
typer runtime.Typer
|
|
yaml bool
|
|
pretty bool
|
|
|
|
data []byte
|
|
defaultGVK *unversioned.GroupVersionKind
|
|
into runtime.Object
|
|
|
|
errFn func(error) bool
|
|
expectedObject runtime.Object
|
|
expectedGVK *unversioned.GroupVersionKind
|
|
}{
|
|
{
|
|
data: []byte("{}"),
|
|
|
|
expectedGVK: &unversioned.GroupVersionKind{},
|
|
errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
|
|
},
|
|
{
|
|
data: []byte("{}"),
|
|
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
creater: &mockCreater{err: fmt.Errorf("fake error")},
|
|
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
errFn: func(err error) bool { return err.Error() == "fake error" },
|
|
},
|
|
{
|
|
data: []byte("{}"),
|
|
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
creater: &mockCreater{err: fmt.Errorf("fake error")},
|
|
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
errFn: func(err error) bool { return err.Error() == "fake error" },
|
|
},
|
|
{
|
|
data: []byte("{}"),
|
|
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
creater: &mockCreater{obj: &testDecodable{}},
|
|
expectedObject: &testDecodable{
|
|
gvk: nil, // json serializer does NOT set GVK
|
|
},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
},
|
|
|
|
// version without group is not defaulted
|
|
{
|
|
data: []byte(`{"apiVersion":"blah"}`),
|
|
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
creater: &mockCreater{obj: &testDecodable{}},
|
|
expectedObject: &testDecodable{
|
|
gvk: nil, // json serializer does NOT set GVK
|
|
},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"},
|
|
},
|
|
// group without version is defaulted
|
|
{
|
|
data: []byte(`{"apiVersion":"other/"}`),
|
|
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
creater: &mockCreater{obj: &testDecodable{}},
|
|
expectedObject: &testDecodable{
|
|
gvk: nil, // json serializer does NOT set GVK
|
|
},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
},
|
|
|
|
// accept runtime.Unknown as into and bypass creator
|
|
{
|
|
data: []byte(`{}`),
|
|
into: &runtime.Unknown{},
|
|
|
|
expectedGVK: &unversioned.GroupVersionKind{},
|
|
expectedObject: &runtime.Unknown{
|
|
RawJSON: []byte(`{}`),
|
|
},
|
|
},
|
|
{
|
|
data: []byte(`{"test":"object"}`),
|
|
into: &runtime.Unknown{},
|
|
|
|
expectedGVK: &unversioned.GroupVersionKind{},
|
|
expectedObject: &runtime.Unknown{
|
|
RawJSON: []byte(`{"test":"object"}`),
|
|
},
|
|
},
|
|
{
|
|
data: []byte(`{"test":"object"}`),
|
|
into: &runtime.Unknown{},
|
|
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
expectedObject: &runtime.Unknown{
|
|
TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
|
|
RawJSON: []byte(`{"test":"object"}`),
|
|
},
|
|
},
|
|
|
|
// unregistered objects can be decoded into directly
|
|
{
|
|
data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`),
|
|
into: &testDecodable{},
|
|
typer: &mockTyper{err: conversion.NewNotRegisteredErr(unversioned.GroupVersionKind{}, nil)},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
expectedObject: &testDecodable{
|
|
Other: "test",
|
|
Value: 1,
|
|
},
|
|
},
|
|
// registered types get defaulted by the into object kind
|
|
{
|
|
data: []byte(`{"value":1,"Other":"test"}`),
|
|
into: &testDecodable{},
|
|
typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
expectedObject: &testDecodable{
|
|
Other: "test",
|
|
Value: 1,
|
|
},
|
|
},
|
|
// registered types get defaulted by the into object kind even without version, but return an error
|
|
{
|
|
data: []byte(`{"value":1,"Other":"test"}`),
|
|
into: &testDecodable{},
|
|
typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: ""},
|
|
errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") },
|
|
expectedObject: &testDecodable{
|
|
Other: "test",
|
|
Value: 1,
|
|
},
|
|
},
|
|
|
|
// runtime.VersionedObjects are decoded
|
|
{
|
|
data: []byte(`{"value":1,"Other":"test"}`),
|
|
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
|
creater: &mockCreater{obj: &testDecodable{}},
|
|
typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
|
|
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
expectedObject: &runtime.VersionedObjects{
|
|
Objects: []runtime.Object{
|
|
&testDecodable{
|
|
Other: "test",
|
|
Value: 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
// runtime.VersionedObjects with an object are decoded into
|
|
{
|
|
data: []byte(`{"Other":"test"}`),
|
|
into: &runtime.VersionedObjects{Objects: []runtime.Object{&testDecodable{Value: 2}}},
|
|
typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
|
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
|
expectedObject: &runtime.VersionedObjects{
|
|
Objects: []runtime.Object{
|
|
&testDecodable{
|
|
Other: "test",
|
|
Value: 2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, test := range testCases {
|
|
var s runtime.Serializer
|
|
if test.yaml {
|
|
s = json.NewYAMLSerializer(json.DefaultMetaFactory, test.creater, test.typer)
|
|
} else {
|
|
s = json.NewSerializer(json.DefaultMetaFactory, test.creater, test.typer, test.pretty)
|
|
}
|
|
obj, gvk, err := s.Decode([]byte(test.data), test.defaultGVK, test.into)
|
|
|
|
if !reflect.DeepEqual(test.expectedGVK, gvk) {
|
|
t.Errorf("%d: unexpected GVK: %v", i, gvk)
|
|
}
|
|
|
|
switch {
|
|
case err == nil && test.errFn != nil:
|
|
t.Errorf("%d: failed: %v", i, err)
|
|
continue
|
|
case err != nil && test.errFn == nil:
|
|
t.Errorf("%d: failed: %v", i, err)
|
|
continue
|
|
case err != nil:
|
|
if !test.errFn(err) {
|
|
t.Errorf("%d: failed: %v", i, err)
|
|
}
|
|
if obj != nil {
|
|
t.Errorf("%d: should have returned nil object", i)
|
|
}
|
|
continue
|
|
}
|
|
|
|
if test.into != nil && test.into != obj {
|
|
t.Errorf("%d: expected into to be returned: %v", i, obj)
|
|
continue
|
|
}
|
|
|
|
if !reflect.DeepEqual(test.expectedObject, obj) {
|
|
t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.expectedObject, obj))
|
|
}
|
|
}
|
|
}
|
|
|
|
type mockCreater struct {
|
|
apiVersion string
|
|
kind string
|
|
err error
|
|
obj runtime.Object
|
|
}
|
|
|
|
func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, error) {
|
|
c.apiVersion, c.kind = kind.GroupVersion().String(), kind.Kind
|
|
return c.obj, c.err
|
|
}
|
|
|
|
type mockTyper struct {
|
|
gvk *unversioned.GroupVersionKind
|
|
err error
|
|
}
|
|
|
|
func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) {
|
|
return t.gvk, false, t.err
|
|
}
|