Commit 6d2c2440 authored by k8s-merge-robot's avatar k8s-merge-robot

Merge pull request #14203 from caesarxuchao/encode-unversioned

Auto commit by PR queue bot
parents 02b99305 ab5c1f67
...@@ -19,6 +19,7 @@ package conversion ...@@ -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 ( ...@@ -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 ...@@ -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 ...@@ -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, ...@@ -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, ...@@ -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. ...@@ -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) { ...@@ -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"}
......
/*
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)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment