mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-09 12:07:47 +00:00
let encoder handle unversioned objects
address lavalamp's comments address lavalamp's comments address lavalamp's comments add a TODO based on lavalamp's comment add a TODO based on lavalamp's comment
This commit is contained in:
parent
9537c43a62
commit
ab5c1f6710
@ -19,6 +19,7 @@ package conversion
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EncodeToVersion turns the given api object into an appropriate JSON string.
|
// EncodeToVersion turns the given api object into an appropriate JSON string.
|
||||||
@ -52,6 +53,14 @@ import (
|
|||||||
func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []byte, err error) {
|
func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []byte, err error) {
|
||||||
obj = maybeCopy(obj)
|
obj = maybeCopy(obj)
|
||||||
v, _ := EnforcePtr(obj) // maybeCopy guarantees a pointer
|
v, _ := EnforcePtr(obj) // maybeCopy guarantees a pointer
|
||||||
|
|
||||||
|
// Don't encode an object defined in the unversioned package, unless if the
|
||||||
|
// destVersion is v1, encode it to v1 for backward compatibility.
|
||||||
|
pkg := path.Base(v.Type().PkgPath())
|
||||||
|
if pkg == "unversioned" && destVersion != "v1" {
|
||||||
|
return s.encodeUnversionedObject(obj)
|
||||||
|
}
|
||||||
|
|
||||||
if _, registered := s.typeToVersion[v.Type()]; !registered {
|
if _, registered := s.typeToVersion[v.Type()]; !registered {
|
||||||
return nil, fmt.Errorf("type %v is not registered for %q and it will be impossible to Decode it, therefore Encode will refuse to encode it.", v.Type(), destVersion)
|
return nil, fmt.Errorf("type %v is not registered for %q and it will be impossible to Decode it, therefore Encode will refuse to encode it.", v.Type(), destVersion)
|
||||||
}
|
}
|
||||||
@ -102,3 +111,21 @@ func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []by
|
|||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Scheme) encodeUnversionedObject(obj interface{}) (data []byte, err error) {
|
||||||
|
_, objKind, err := s.ObjectVersionAndKind(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = s.SetVersionAndKind("", objKind, obj); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err = json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Version and Kind should be blank in memory. Reset them, since it's
|
||||||
|
// possible that we modified a user object and not a copy above.
|
||||||
|
err = s.SetVersionAndKind("", "", obj)
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@ package conversion
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ func UpdateVersionAndKind(baseFields []string, versionField, version, kindField,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
pkg := path.Base(v.Type().PkgPath())
|
||||||
t := v.Type()
|
t := v.Type()
|
||||||
name := t.Name()
|
name := t.Name()
|
||||||
if v.Kind() != reflect.Struct {
|
if v.Kind() != reflect.Struct {
|
||||||
@ -93,6 +95,15 @@ func UpdateVersionAndKind(baseFields []string, versionField, version, kindField,
|
|||||||
|
|
||||||
field := v.FieldByName(kindField)
|
field := v.FieldByName(kindField)
|
||||||
if !field.IsValid() {
|
if !field.IsValid() {
|
||||||
|
// Types defined in the unversioned package are allowed to not have a
|
||||||
|
// kindField. Clients will have to know what they are based on the
|
||||||
|
// context.
|
||||||
|
// TODO: add some type trait here, or some way of indicating whether
|
||||||
|
// this feature is allowed on a per-type basis. Using package name is
|
||||||
|
// overly broad and a bit hacky.
|
||||||
|
if pkg == "unversioned" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return fmt.Errorf("couldn't find %v field in %#v", kindField, v.Interface())
|
return fmt.Errorf("couldn't find %v field in %#v", kindField, v.Interface())
|
||||||
}
|
}
|
||||||
field.SetString(kind)
|
field.SetString(kind)
|
||||||
|
@ -17,8 +17,11 @@ limitations under the License.
|
|||||||
package conversion
|
package conversion
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSimpleMetaFactoryInterpret(t *testing.T) {
|
func TestSimpleMetaFactoryInterpret(t *testing.T) {
|
||||||
@ -72,6 +75,25 @@ func TestSimpleMetaFactoryUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test Updating objects that don't have a Kind field.
|
||||||
|
func TestSimpleMetaFactoryUpdateNoKindField(t *testing.T) {
|
||||||
|
factory := SimpleMetaFactory{VersionField: "APIVersion", KindField: "Kind"}
|
||||||
|
// obj does not have a Kind field and is not defined in the unversioned package.
|
||||||
|
obj := struct {
|
||||||
|
SomeField string
|
||||||
|
}{"1"}
|
||||||
|
expectedError := fmt.Errorf("couldn't find %v field in %#v", factory.KindField, obj)
|
||||||
|
if err := factory.Update("test", "other", &obj); err == nil || expectedError.Error() != err.Error() {
|
||||||
|
t.Fatalf("expected error: %v, got: %v", expectedError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMeta does not have a Kind field, but is defined in the unversioned package.
|
||||||
|
listMeta := unversioned.ListMeta{}
|
||||||
|
if err := factory.Update("test", "other", &listMeta); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSimpleMetaFactoryUpdateStruct(t *testing.T) {
|
func TestSimpleMetaFactoryUpdateStruct(t *testing.T) {
|
||||||
factory := SimpleMetaFactory{BaseFields: []string{"Test"}, VersionField: "V", KindField: "K"}
|
factory := SimpleMetaFactory{BaseFields: []string{"Test"}, VersionField: "V", KindField: "K"}
|
||||||
|
|
||||||
|
92
pkg/conversion/unversioned_test.go
Normal file
92
pkg/conversion/unversioned_test.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
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 conversion_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
// TODO: Ideally we should create the necessary package structure in e.g.,
|
||||||
|
// pkg/conversion/test/... instead of importing pkg/api here.
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var status = &unversioned.Status{
|
||||||
|
Status: unversioned.StatusFailure,
|
||||||
|
Code: 200,
|
||||||
|
Reason: unversioned.StatusReasonUnknown,
|
||||||
|
Message: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestV1EncodeDecodeStatus(t *testing.T) {
|
||||||
|
|
||||||
|
v1Codec := testapi.Default.Codec()
|
||||||
|
|
||||||
|
encoded, err := v1Codec.Encode(status)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
typeMeta := unversioned.TypeMeta{}
|
||||||
|
if err := json.Unmarshal(encoded, &typeMeta); err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if typeMeta.Kind != "Status" {
|
||||||
|
t.Errorf("Kind is not set to \"Status\". Got %v", string(encoded))
|
||||||
|
}
|
||||||
|
if typeMeta.APIVersion != "v1" {
|
||||||
|
t.Errorf("APIVersion is not set to \"v1\". Got %v", string(encoded))
|
||||||
|
}
|
||||||
|
decoded, err := v1Codec.Decode(encoded)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(status, decoded) {
|
||||||
|
t.Errorf("expected: %v, got: %v", status, decoded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExperimentalEncodeDecodeStatus(t *testing.T) {
|
||||||
|
// TODO: caesarxuchao: use the testapi.Experimental.Codec() once the PR that
|
||||||
|
// moves experimental from v1 to v1alpha1 got merged.
|
||||||
|
// expCodec := testapi.Experimental.Codec()
|
||||||
|
expCodec := runtime.CodecFor(api.Scheme, "experimental/v1alpha1")
|
||||||
|
encoded, err := expCodec.Encode(status)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
typeMeta := unversioned.TypeMeta{}
|
||||||
|
if err := json.Unmarshal(encoded, &typeMeta); err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if typeMeta.Kind != "Status" {
|
||||||
|
t.Errorf("Kind is not set to \"Status\". Got %s", encoded)
|
||||||
|
}
|
||||||
|
if typeMeta.APIVersion != "" {
|
||||||
|
t.Errorf("APIVersion is not set to \"\". Got %s", encoded)
|
||||||
|
}
|
||||||
|
decoded, err := expCodec.Decode(encoded)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(status, decoded) {
|
||||||
|
t.Errorf("expected: %v, got: %v", status, decoded)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user