Move unstructured to its own package under v1

It is a versioned type.
This commit is contained in:
Clayton Coleman 2016-11-02 21:54:13 -04:00
parent bda57b8fb6
commit 8eb3e9a518
No known key found for this signature in database
GPG Key ID: 3D16906B4F1C5CB3
8 changed files with 102 additions and 54 deletions

View File

@ -20,6 +20,7 @@ import (
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/apis/meta/v1/unstructured"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
)
@ -29,7 +30,7 @@ func IsListType(obj runtime.Object) bool {
// if we're a runtime.Unstructured, check to see if we have an `items` key
// This is a list type for recognition, but other Items type methods will fail on it
// and give you errors.
if unstructured, ok := obj.(*runtime.Unstructured); ok {
if unstructured, ok := obj.(*unstructured.Unstructured); ok {
_, ok := unstructured.Object["items"]
return ok
}

View File

@ -17,7 +17,7 @@ limitations under the License.
package meta
import (
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/meta/v1/unstructured"
"k8s.io/kubernetes/pkg/runtime/schema"
)
@ -25,7 +25,7 @@ import (
// dealing with runtime.Unstructured objects.
func InterfacesForUnstructured(schema.GroupVersion) (*VersionInterfaces, error) {
return &VersionInterfaces{
ObjectConvertor: &runtime.UnstructuredObjectConverter{},
ObjectConvertor: &unstructured.UnstructuredObjectConverter{},
MetadataAccessor: NewAccessor(),
}, nil
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime
package unstructured
import (
"bytes"
@ -26,13 +26,59 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api/meta/metatypes"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/json"
)
// Unstructured allows objects that do not have Golang structs registered to be manipulated
// generically. This can be used to deal with the API objects from a plug-in. Unstructured
// objects still have functioning TypeMeta features-- kind, version, etc.
//
// WARNING: This object has accessors for the v1 standard metadata. You *MUST NOT* use this
// type if you are dealing with objects that are not in the server meta v1 schema.
//
// TODO: make the serialization part of this type distinct from the field accessors.
type Unstructured struct {
// Object is a JSON compatible map with string, float, int, bool, []interface{}, or
// map[string]interface{}
// children.
Object map[string]interface{}
}
var _ runtime.Unstructured = &Unstructured{}
var _ runtime.Unstructured = &UnstructuredList{}
func (obj *Unstructured) GetObjectKind() schema.ObjectKind { return obj }
func (obj *UnstructuredList) GetObjectKind() schema.ObjectKind { return obj }
func (obj *Unstructured) IsUnstructuredObject() {}
func (obj *UnstructuredList) IsUnstructuredObject() {}
func (obj *Unstructured) IsList() bool {
if obj.Object != nil {
_, ok := obj.Object["items"]
return ok
}
return false
}
func (obj *UnstructuredList) IsList() bool { return true }
func (obj *Unstructured) UnstructuredContent() map[string]interface{} {
if obj.Object == nil {
obj.Object = make(map[string]interface{})
}
return obj.Object
}
func (obj *UnstructuredList) UnstructuredContent() map[string]interface{} {
if obj.Object == nil {
obj.Object = make(map[string]interface{})
}
return obj.Object
}
// MarshalJSON ensures that the unstructured object produces proper
// JSON when passed to Go's standard JSON library.
func (u *Unstructured) MarshalJSON() ([]byte, error) {
@ -142,7 +188,7 @@ func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) {
setNestedMap(u.Object, value, fields...)
}
func extractOwnerReference(src interface{}) metatypes.OwnerReference {
func extractOwnerReference(src interface{}) metav1.OwnerReference {
v := src.(map[string]interface{})
controllerPtr, ok := (getNestedField(v, "controller")).(*bool)
if !ok {
@ -153,7 +199,7 @@ func extractOwnerReference(src interface{}) metatypes.OwnerReference {
controllerPtr = &controller
}
}
return metatypes.OwnerReference{
return metav1.OwnerReference{
Kind: getNestedString(v, "kind"),
Name: getNestedString(v, "name"),
APIVersion: getNestedString(v, "apiVersion"),
@ -162,7 +208,7 @@ func extractOwnerReference(src interface{}) metatypes.OwnerReference {
}
}
func setOwnerReference(src metatypes.OwnerReference) map[string]interface{} {
func setOwnerReference(src metav1.OwnerReference) map[string]interface{} {
ret := make(map[string]interface{})
controllerPtr := src.Controller
if controllerPtr != nil {
@ -202,20 +248,20 @@ func getOwnerReferences(object map[string]interface{}) ([]map[string]interface{}
return ownerReferences, nil
}
func (u *Unstructured) GetOwnerReferences() []metatypes.OwnerReference {
func (u *Unstructured) GetOwnerReferences() []metav1.OwnerReference {
original, err := getOwnerReferences(u.Object)
if err != nil {
glog.V(6).Info(err)
return nil
}
ret := make([]metatypes.OwnerReference, 0, len(original))
ret := make([]metav1.OwnerReference, 0, len(original))
for i := 0; i < len(original); i++ {
ret = append(ret, extractOwnerReference(original[i]))
}
return ret
}
func (u *Unstructured) SetOwnerReferences(references []metatypes.OwnerReference) {
func (u *Unstructured) SetOwnerReferences(references []metav1.OwnerReference) {
var newReferences = make([]map[string]interface{}, 0, len(references))
for i := 0; i < len(references); i++ {
newReferences = append(newReferences, setOwnerReference(references[i]))
@ -439,11 +485,11 @@ func (u *UnstructuredList) GroupVersionKind() schema.GroupVersionKind {
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
// type, which can be used for generic access to objects without a predefined scheme.
// TODO: move into serializer/json.
var UnstructuredJSONScheme Codec = unstructuredJSONScheme{}
var UnstructuredJSONScheme runtime.Codec = unstructuredJSONScheme{}
type unstructuredJSONScheme struct{}
func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj Object) (Object, *schema.GroupVersionKind, error) {
func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
var err error
if obj != nil {
err = s.decodeInto(data, obj)
@ -457,13 +503,13 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind,
gvk := obj.GetObjectKind().GroupVersionKind()
if len(gvk.Kind) == 0 {
return nil, &gvk, NewMissingKindErr(string(data))
return nil, &gvk, runtime.NewMissingKindErr(string(data))
}
return obj, &gvk, nil
}
func (unstructuredJSONScheme) Encode(obj Object, w io.Writer) error {
func (unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
switch t := obj.(type) {
case *Unstructured:
return json.NewEncoder(w).Encode(t.Object)
@ -475,7 +521,7 @@ func (unstructuredJSONScheme) Encode(obj Object, w io.Writer) error {
t.Object["items"] = items
defer func() { delete(t.Object, "items") }()
return json.NewEncoder(w).Encode(t.Object)
case *Unknown:
case *runtime.Unknown:
// TODO: Unstructured needs to deal with ContentType.
_, err := w.Write(t.Raw)
return err
@ -484,7 +530,7 @@ func (unstructuredJSONScheme) Encode(obj Object, w io.Writer) error {
}
}
func (s unstructuredJSONScheme) decode(data []byte) (Object, error) {
func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) {
type detector struct {
Items gojson.RawMessage
}
@ -505,16 +551,16 @@ func (s unstructuredJSONScheme) decode(data []byte) (Object, error) {
return unstruct, err
}
func (s unstructuredJSONScheme) decodeInto(data []byte, obj Object) error {
func (s unstructuredJSONScheme) decodeInto(data []byte, obj runtime.Object) error {
switch x := obj.(type) {
case *Unstructured:
return s.decodeToUnstructured(data, x)
case *UnstructuredList:
return s.decodeToList(data, x)
case *VersionedObjects:
case *runtime.VersionedObjects:
o, err := s.decode(data)
if err == nil {
x.Objects = []Object{o}
x.Objects = []runtime.Object{o}
}
return err
default:
@ -596,7 +642,7 @@ func (UnstructuredObjectConverter) Convert(in, out, context interface{}) error {
return nil
}
func (UnstructuredObjectConverter) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
func (UnstructuredObjectConverter) ConvertToVersion(in runtime.Object, target runtime.GroupVersioner) (runtime.Object, error) {
if kind := in.GetObjectKind().GroupVersionKind(); !kind.Empty() {
gvk, ok := target.KindForGroupVersionKinds([]schema.GroupVersionKind{kind})
if !ok {

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime_test
package unstructured_test
import (
"fmt"
@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apimachinery/registered"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/apis/meta/v1/unstructured"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/types"
)
@ -49,7 +50,7 @@ func TestDecodeUnstructured(t *testing.T) {
Raw: []byte(rawJson),
ContentType: runtime.ContentTypeJSON,
},
&runtime.Unstructured{
&unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "Foo",
"apiVersion": "Bar",
@ -58,13 +59,13 @@ func TestDecodeUnstructured(t *testing.T) {
},
},
}
if errs := runtime.DecodeList(pl.Items, runtime.UnstructuredJSONScheme); len(errs) == 1 {
if errs := runtime.DecodeList(pl.Items, unstructured.UnstructuredJSONScheme); len(errs) == 1 {
t.Fatalf("unexpected error %v", errs)
}
if pod, ok := pl.Items[1].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
if pod, ok := pl.Items[1].(*unstructured.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
t.Errorf("object not converted: %#v", pl.Items[1])
}
if pod, ok := pl.Items[2].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
if pod, ok := pl.Items[2].(*unstructured.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
t.Errorf("object not converted: %#v", pl.Items[2])
}
}
@ -76,21 +77,21 @@ func TestDecode(t *testing.T) {
}{
{
json: []byte(`{"apiVersion": "test", "kind": "test_kind"}`),
want: &runtime.Unstructured{
want: &unstructured.Unstructured{
Object: map[string]interface{}{"apiVersion": "test", "kind": "test_kind"},
},
},
{
json: []byte(`{"apiVersion": "test", "kind": "test_list", "items": []}`),
want: &runtime.UnstructuredList{
want: &unstructured.UnstructuredList{
Object: map[string]interface{}{"apiVersion": "test", "kind": "test_list"},
},
},
{
json: []byte(`{"items": [{"metadata": {"name": "object1"}, "apiVersion": "test", "kind": "test_kind"}, {"metadata": {"name": "object2"}, "apiVersion": "test", "kind": "test_kind"}], "apiVersion": "test", "kind": "test_list"}`),
want: &runtime.UnstructuredList{
want: &unstructured.UnstructuredList{
Object: map[string]interface{}{"apiVersion": "test", "kind": "test_list"},
Items: []*runtime.Unstructured{
Items: []*unstructured.Unstructured{
{
Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "object1"},
@ -111,7 +112,7 @@ func TestDecode(t *testing.T) {
}
for _, tc := range tcs {
got, _, err := runtime.UnstructuredJSONScheme.Decode(tc.json, nil, nil)
got, _, err := unstructured.UnstructuredJSONScheme.Decode(tc.json, nil, nil)
if err != nil {
t.Errorf("Unexpected error for %q: %v", string(tc.json), err)
continue
@ -124,7 +125,7 @@ func TestDecode(t *testing.T) {
}
func TestUnstructuredGetters(t *testing.T) {
unstruct := runtime.Unstructured{
unstruct := unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "test_kind",
"apiVersion": "test_version",
@ -240,10 +241,10 @@ func TestUnstructuredGetters(t *testing.T) {
}
func TestUnstructuredSetters(t *testing.T) {
unstruct := runtime.Unstructured{}
unstruct := unstructured.Unstructured{}
trueVar := true
want := runtime.Unstructured{
want := unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "test_kind",
"apiVersion": "test_version",
@ -325,7 +326,7 @@ func TestUnstructuredSetters(t *testing.T) {
}
func TestUnstructuredListGetters(t *testing.T) {
unstruct := runtime.UnstructuredList{
unstruct := unstructured.UnstructuredList{
Object: map[string]interface{}{
"kind": "test_kind",
"apiVersion": "test_version",
@ -354,9 +355,9 @@ func TestUnstructuredListGetters(t *testing.T) {
}
func TestUnstructuredListSetters(t *testing.T) {
unstruct := runtime.UnstructuredList{}
unstruct := unstructured.UnstructuredList{}
want := runtime.UnstructuredList{
want := unstructured.UnstructuredList{
Object: map[string]interface{}{
"kind": "test_kind",
"apiVersion": "test_version",
@ -407,11 +408,11 @@ func TestDecodeNumbers(t *testing.T) {
}
// Round-trip with unstructured codec
unstructuredObj, err := runtime.Decode(runtime.UnstructuredJSONScheme, originalJSON)
unstructuredObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalJSON)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
roundtripJSON, err := runtime.Encode(runtime.UnstructuredJSONScheme, unstructuredObj)
roundtripJSON, err := runtime.Encode(unstructured.UnstructuredJSONScheme, unstructuredObj)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@ -235,3 +235,17 @@ type SelfLinker interface {
type Object interface {
GetObjectKind() schema.ObjectKind
}
// Unstructured objects store values as map[string]interface{}, with only values that can be serialized
// to JSON allowed.
type Unstructured interface {
// IsUnstructuredObject is a marker interface to allow objects that can be serialized but not introspected
// to bypass conversion.
IsUnstructuredObject()
// IsList returns true if this type is a list or matches the list convention - has an array called "items".
IsList() bool
// UnstructuredContent returns a non-nil, mutable map of the contents of this object. Values may be
// []interface{}, map[string]interface{}, or any primitive type. Contents are typically serialized to
// and from JSON.
UnstructuredContent() map[string]interface{}
}

View File

@ -30,9 +30,6 @@ func (obj *TypeMeta) GroupVersionKind() schema.GroupVersionKind {
func (obj *Unknown) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *Unstructured) GetObjectKind() schema.ObjectKind { return obj }
func (obj *UnstructuredList) GetObjectKind() schema.ObjectKind { return obj }
// GetObjectKind implements Object for VersionedObjects, returning an empty ObjectKind
// interface if no objects are provided, or the ObjectKind interface of the object in the
// highest array position.

View File

@ -181,7 +181,7 @@ func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into ru
// 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 obj.(type) {
case *runtime.Unknown, *runtime.Unstructured, *runtime.UnstructuredList:
case *runtime.Unknown, runtime.Unstructured:
return c.encoder.Encode(obj, w)
}

View File

@ -122,17 +122,6 @@ type Unknown struct {
ContentType string `protobuf:"bytes,4,opt,name=contentType"`
}
// Unstructured allows objects that do not have Golang structs registered to be manipulated
// generically. This can be used to deal with the API objects from a plug-in. Unstructured
// objects still have functioning TypeMeta features-- kind, version, etc.
// TODO: Make this object have easy access to field based accessors and settors for
// metadata and field mutatation.
type Unstructured struct {
// Object is a JSON compatible map with string, float, int, []interface{}, or map[string]interface{}
// children.
Object map[string]interface{}
}
// VersionedObjects is used by Decoders to give callers a way to access all versions
// of an object during the decoding process.
type VersionedObjects struct {