From 28132be07edd2041a9de70e780a1ac239c839226 Mon Sep 17 00:00:00 2001 From: Kris Date: Mon, 25 Apr 2016 13:34:35 -0700 Subject: [PATCH] Redo Unstructured to have accessor methods Add accessor methods that implement pkg/api/unversioned.ObjectKind, pkg/api/meta.Object, pkg/api/meta.Type and pkg/api/meta.List. Removed the convenience fields since writing to them was not reflected in serialized JSON. --- pkg/api/meta/interfaces.go | 2 + pkg/client/typed/dynamic/client.go | 4 +- pkg/client/typed/dynamic/client_test.go | 17 +- .../namespace/namespace_controller_utils.go | 2 +- pkg/runtime/helper_test.go | 8 +- pkg/runtime/register.go | 7 +- pkg/runtime/types.go | 250 +++++++++++++++++- pkg/runtime/unstructured.go | 42 +-- pkg/runtime/unstructured_test.go | 206 +++++++++++++-- 9 files changed, 460 insertions(+), 78 deletions(-) diff --git a/pkg/api/meta/interfaces.go b/pkg/api/meta/interfaces.go index 8001d57e84..ffebcf3436 100644 --- a/pkg/api/meta/interfaces.go +++ b/pkg/api/meta/interfaces.go @@ -177,3 +177,5 @@ type RESTMapper interface { AliasesForResource(resource string) ([]string, bool) ResourceSingularizer(resource string) (singular string, err error) } + +var _ Object = &runtime.Unstructured{} diff --git a/pkg/client/typed/dynamic/client.go b/pkg/client/typed/dynamic/client.go index b46c79b683..355a2be357 100644 --- a/pkg/client/typed/dynamic/client.go +++ b/pkg/client/typed/dynamic/client.go @@ -157,12 +157,12 @@ func (rc *ResourceClient) Create(obj *runtime.Unstructured) (*runtime.Unstructur // Update updates the provided resource. func (rc *ResourceClient) Update(obj *runtime.Unstructured) (*runtime.Unstructured, error) { result := new(runtime.Unstructured) - if len(obj.Name) == 0 { + if len(obj.GetName()) == 0 { return result, errors.New("object missing name") } err := rc.namespace(rc.cl.Put()). Resource(rc.resource.Name). - Name(obj.Name). + Name(obj.GetName()). Body(obj). Do(). Into(result) diff --git a/pkg/client/typed/dynamic/client_test.go b/pkg/client/typed/dynamic/client_test.go index 1c4965ec44..0e1b477732 100644 --- a/pkg/client/typed/dynamic/client_test.go +++ b/pkg/client/typed/dynamic/client_test.go @@ -45,11 +45,6 @@ func getListJSON(version, kind string, items ...[]byte) []byte { func getObject(version, kind, name string) *runtime.Unstructured { return &runtime.Unstructured{ - TypeMeta: runtime.TypeMeta{ - APIVersion: version, - Kind: kind, - }, - Name: name, Object: map[string]interface{}{ "apiVersion": version, "kind": kind, @@ -88,9 +83,9 @@ func TestList(t *testing.T) { getJSON("vTest", "rTest", "item1"), getJSON("vTest", "rTest", "item2")), want: &runtime.UnstructuredList{ - TypeMeta: runtime.TypeMeta{ - APIVersion: "vTest", - Kind: "rTestList", + Object: map[string]interface{}{ + "apiVersion": "vTest", + "kind": "rTestList", }, Items: []*runtime.Unstructured{ getObject("vTest", "rTest", "item1"), @@ -106,9 +101,9 @@ func TestList(t *testing.T) { getJSON("vTest", "rTest", "item1"), getJSON("vTest", "rTest", "item2")), want: &runtime.UnstructuredList{ - TypeMeta: runtime.TypeMeta{ - APIVersion: "vTest", - Kind: "rTestList", + Object: map[string]interface{}{ + "apiVersion": "vTest", + "kind": "rTestList", }, Items: []*runtime.Unstructured{ getObject("vTest", "rTest", "item1"), diff --git a/pkg/controller/namespace/namespace_controller_utils.go b/pkg/controller/namespace/namespace_controller_utils.go index 4b86b7d739..dcc8b243d0 100644 --- a/pkg/controller/namespace/namespace_controller_utils.go +++ b/pkg/controller/namespace/namespace_controller_utils.go @@ -236,7 +236,7 @@ func deleteEachItem( } apiResource := unversioned.APIResource{Name: gvr.Resource, Namespaced: true} for _, item := range unstructuredList.Items { - if err = dynamicClient.Resource(&apiResource, namespace).Delete(item.Name, nil); err != nil && !errors.IsNotFound(err) && !errors.IsMethodNotSupported(err) { + if err = dynamicClient.Resource(&apiResource, namespace).Delete(item.GetName(), nil); err != nil && !errors.IsNotFound(err) && !errors.IsMethodNotSupported(err) { return err } } diff --git a/pkg/runtime/helper_test.go b/pkg/runtime/helper_test.go index a4ab5071c2..e15a0e799f 100644 --- a/pkg/runtime/helper_test.go +++ b/pkg/runtime/helper_test.go @@ -33,7 +33,13 @@ func TestDecodeList(t *testing.T) { Raw: []byte(`{"kind":"Pod","apiVersion":"` + testapi.Default.GroupVersion().String() + `","metadata":{"name":"test"}}`), ContentType: runtime.ContentTypeJSON, }, - &runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}}, + &runtime.Unstructured{ + Object: map[string]interface{}{ + "kind": "Foo", + "apiVersion": "Bar", + "test": "value", + }, + }, }, } if errs := runtime.DecodeList(pl.Items, testapi.Default.Codec()); len(errs) != 0 { diff --git a/pkg/runtime/register.go b/pkg/runtime/register.go index ec58b345de..07d4196308 100644 --- a/pkg/runtime/register.go +++ b/pkg/runtime/register.go @@ -30,9 +30,10 @@ func (obj *TypeMeta) GroupVersionKind() *unversioned.GroupVersionKind { return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind) } -func (obj *Unknown) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *Unstructured) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *UnstructuredList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *Unknown) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } + +func (obj *Unstructured) GetObjectKind() unversioned.ObjectKind { return obj } +func (obj *UnstructuredList) GetObjectKind() unversioned.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 diff --git a/pkg/runtime/types.go b/pkg/runtime/types.go index 313a00716c..44a309e782 100644 --- a/pkg/runtime/types.go +++ b/pkg/runtime/types.go @@ -16,6 +16,11 @@ limitations under the License. package runtime +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/types" +) + // Note that the types provided in this file are not versioned and are intended to be // safe to use from within all versions of every API object. @@ -120,26 +125,259 @@ type Unknown struct { // TODO: Make this object have easy access to field based accessors and settors for // metadata and field mutatation. type Unstructured struct { - TypeMeta `json:",inline"` - - // Name is populated from metadata (if present) upon deserialization - Name string - // Object is a JSON compatible map with string, float, int, []interface{}, or map[string]interface{} // children. Object map[string]interface{} } +func getNestedField(obj map[string]interface{}, fields ...string) interface{} { + var val interface{} = obj + for _, field := range fields { + if _, ok := val.(map[string]interface{}); !ok { + return nil + } + val = val.(map[string]interface{})[field] + } + return val +} + +func getNestedString(obj map[string]interface{}, fields ...string) string { + if str, ok := getNestedField(obj, fields...).(string); ok { + return str + } + return "" +} + +func getNestedMap(obj map[string]interface{}, fields ...string) map[string]string { + if m, ok := getNestedField(obj, fields...).(map[string]interface{}); ok { + strMap := make(map[string]string, len(m)) + for k, v := range m { + if str, ok := v.(string); ok { + strMap[k] = str + } + } + return strMap + } + return nil +} + +func setNestedField(obj map[string]interface{}, value interface{}, fields ...string) { + m := obj + if len(fields) > 1 { + for _, field := range fields[0 : len(fields)-1] { + if _, ok := m[field].(map[string]interface{}); !ok { + m[field] = make(map[string]interface{}) + } + m = m[field].(map[string]interface{}) + } + } + m[fields[len(fields)-1]] = value +} + +func setNestedMap(obj map[string]interface{}, value map[string]string, fields ...string) { + m := make(map[string]interface{}, len(value)) + for k, v := range value { + m[k] = v + } + setNestedField(obj, m, fields...) +} + +func (u *Unstructured) setNestedField(value interface{}, fields ...string) { + if u.Object == nil { + u.Object = make(map[string]interface{}) + } + setNestedField(u.Object, value, fields...) +} + +func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) { + if u.Object == nil { + u.Object = make(map[string]interface{}) + } + setNestedMap(u.Object, value, fields...) +} + +func (u *Unstructured) GetAPIVersion() string { + return getNestedString(u.Object, "apiVersion") +} + +func (u *Unstructured) SetAPIVersion(version string) { + u.setNestedField(version, "apiVersion") +} + +func (u *Unstructured) GetKind() string { + return getNestedString(u.Object, "kind") +} + +func (u *Unstructured) SetKind(kind string) { + u.setNestedField(kind, "kind") +} + +func (u *Unstructured) GetNamespace() string { + return getNestedString(u.Object, "metadata", "namespace") +} + +func (u *Unstructured) SetNamespace(namespace string) { + u.setNestedField(namespace, "metadata", "namespace") +} + +func (u *Unstructured) GetName() string { + return getNestedString(u.Object, "metadata", "name") +} + +func (u *Unstructured) SetName(name string) { + u.setNestedField(name, "metadata", "name") +} + +func (u *Unstructured) GetGenerateName() string { + return getNestedString(u.Object, "metadata", "generateName") +} + +func (u *Unstructured) SetGenerateName(name string) { + u.setNestedField(name, "metadata", "generateName") +} + +func (u *Unstructured) GetUID() types.UID { + return types.UID(getNestedString(u.Object, "metadata", "uid")) +} + +func (u *Unstructured) SetUID(uid types.UID) { + u.setNestedField(string(uid), "metadata", "uid") +} + +func (u *Unstructured) GetResourceVersion() string { + return getNestedString(u.Object, "metadata", "resourceVersion") +} + +func (u *Unstructured) SetResourceVersion(version string) { + u.setNestedField(version, "metadata", "resourceVersion") +} + +func (u *Unstructured) GetSelfLink() string { + return getNestedString(u.Object, "metadata", "selfLink") +} + +func (u *Unstructured) SetSelfLink(selfLink string) { + u.setNestedField(selfLink, "metadata", "selfLink") +} + +func (u *Unstructured) GetCreationTimestamp() unversioned.Time { + var timestamp unversioned.Time + timestamp.UnmarshalQueryParameter(getNestedString(u.Object, "metadata", "creationTimestamp")) + return timestamp +} + +func (u *Unstructured) SetCreationTimestamp(timestamp unversioned.Time) { + ts, _ := timestamp.MarshalQueryParameter() + u.setNestedField(ts, "metadata", "creationTimestamp") +} + +func (u *Unstructured) GetDeletionTimestamp() *unversioned.Time { + var timestamp unversioned.Time + timestamp.UnmarshalQueryParameter(getNestedString(u.Object, "metadata", "deletionTimestamp")) + if timestamp.IsZero() { + return nil + } + return ×tamp +} + +func (u *Unstructured) SetDeletionTimestamp(timestamp *unversioned.Time) { + ts, _ := timestamp.MarshalQueryParameter() + u.setNestedField(ts, "metadata", "deletionTimestamp") +} + +func (u *Unstructured) GetLabels() map[string]string { + return getNestedMap(u.Object, "metadata", "labels") +} + +func (u *Unstructured) SetLabels(labels map[string]string) { + u.setNestedMap(labels, "metadata", "labels") +} + +func (u *Unstructured) GetAnnotations() map[string]string { + return getNestedMap(u.Object, "metadata", "annotations") +} + +func (u *Unstructured) SetAnnotations(annotations map[string]string) { + u.setNestedMap(annotations, "metadata", "annotations") +} + +func (u *Unstructured) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { + u.SetAPIVersion(gvk.GroupVersion().String()) + u.SetKind(gvk.Kind) +} + +func (u *Unstructured) GroupVersionKind() *unversioned.GroupVersionKind { + gv, err := unversioned.ParseGroupVersion(u.GetAPIVersion()) + if err != nil { + return nil + } + gvk := gv.WithKind(u.GetKind()) + return &gvk +} + // UnstructuredList allows lists that do not have Golang structs // registered to be manipulated generically. This can be used to deal // with the API lists from a plug-in. type UnstructuredList struct { - TypeMeta `json:",inline"` + Object map[string]interface{} // Items is a list of unstructured objects. Items []*Unstructured `json:"items"` } +func (u *UnstructuredList) setNestedField(value interface{}, fields ...string) { + if u.Object == nil { + u.Object = make(map[string]interface{}) + } + setNestedField(u.Object, value, fields...) +} + +func (u *UnstructuredList) GetAPIVersion() string { + return getNestedString(u.Object, "apiVersion") +} + +func (u *UnstructuredList) SetAPIVersion(version string) { + u.setNestedField(version, "apiVersion") +} + +func (u *UnstructuredList) GetKind() string { + return getNestedString(u.Object, "kind") +} + +func (u *UnstructuredList) SetKind(kind string) { + u.setNestedField(kind, "kind") +} + +func (u *UnstructuredList) GetResourceVersion() string { + return getNestedString(u.Object, "metadata", "resourceVersion") +} + +func (u *UnstructuredList) SetResourceVersion(version string) { + u.setNestedField(version, "metadata", "resourceVersion") +} + +func (u *UnstructuredList) GetSelfLink() string { + return getNestedString(u.Object, "metadata", "selfLink") +} + +func (u *UnstructuredList) SetSelfLink(selfLink string) { + u.setNestedField(selfLink, "metadata", "selfLink") +} + +func (u *UnstructuredList) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { + u.SetAPIVersion(gvk.GroupVersion().String()) + u.SetKind(gvk.Kind) +} + +func (u *UnstructuredList) GroupVersionKind() *unversioned.GroupVersionKind { + gv, err := unversioned.ParseGroupVersion(u.GetAPIVersion()) + if err != nil { + return nil + } + gvk := gv.WithKind(u.GetKind()) + return &gvk +} + // VersionedObjects is used by Decoders to give callers a way to access all versions // of an object during the decoding process. type VersionedObjects struct { diff --git a/pkg/runtime/unstructured.go b/pkg/runtime/unstructured.go index ba10aabf16..8c39a5f3b6 100644 --- a/pkg/runtime/unstructured.go +++ b/pkg/runtime/unstructured.go @@ -56,17 +56,13 @@ func (unstructuredJSONScheme) EncodeToStream(obj Object, w io.Writer, overrides case *Unstructured: return json.NewEncoder(w).Encode(t.Object) case *UnstructuredList: - type encodeList struct { - TypeMeta `json:",inline"` - Items []map[string]interface{} `json:"items"` - } - eList := encodeList{ - TypeMeta: t.TypeMeta, - } + var items []map[string]interface{} for _, i := range t.Items { - eList.Items = append(eList.Items, i.Object) + items = append(items, i.Object) } - return json.NewEncoder(w).Encode(eList) + t.Object["items"] = items + defer func() { delete(t.Object, "items") }() + return json.NewEncoder(w).Encode(t.Object) case *Unknown: // TODO: Unstructured needs to deal with ContentType. _, err := w.Write(t.Raw) @@ -113,25 +109,6 @@ func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstru return err } - if v, ok := m["kind"]; ok { - if s, ok := v.(string); ok { - unstruct.Kind = s - } - } - if v, ok := m["apiVersion"]; ok { - if s, ok := v.(string); ok { - unstruct.APIVersion = s - } - } - if metadata, ok := m["metadata"]; ok { - if metadata, ok := metadata.(map[string]interface{}); ok { - if name, ok := metadata["name"]; ok { - if name, ok := name.(string); ok { - unstruct.Name = name - } - } - } - } unstruct.Object = m return nil @@ -139,8 +116,7 @@ func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstru func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList) error { type decodeList struct { - TypeMeta `json:",inline"` - Items []gojson.RawMessage + Items []gojson.RawMessage } var dList decodeList @@ -148,7 +124,11 @@ func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList return err } - list.TypeMeta = dList.TypeMeta + if err := json.Unmarshal(data, &list.Object); err != nil { + return err + } + + delete(list.Object, "items") list.Items = nil for _, i := range dList.Items { unstruct := &Unstructured{} diff --git a/pkg/runtime/unstructured_test.go b/pkg/runtime/unstructured_test.go index fee7fbe3ae..155d534ff1 100644 --- a/pkg/runtime/unstructured_test.go +++ b/pkg/runtime/unstructured_test.go @@ -21,11 +21,14 @@ import ( "reflect" "strings" "testing" + "time" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/types" ) func TestDecodeUnstructured(t *testing.T) { @@ -44,7 +47,13 @@ func TestDecodeUnstructured(t *testing.T) { Raw: []byte(rawJson), ContentType: runtime.ContentTypeJSON, }, - &runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}}, + &runtime.Unstructured{ + Object: map[string]interface{}{ + "kind": "Foo", + "apiVersion": "Bar", + "test": "value", + }, + }, }, } if errs := runtime.DecodeList(pl.Items, runtime.UnstructuredJSONScheme); len(errs) == 1 { @@ -66,36 +75,21 @@ func TestDecode(t *testing.T) { { json: []byte(`{"apiVersion": "test", "kind": "test_kind"}`), want: &runtime.Unstructured{ - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_kind", - }, Object: map[string]interface{}{"apiVersion": "test", "kind": "test_kind"}, }, }, { json: []byte(`{"apiVersion": "test", "kind": "test_list", "items": []}`), want: &runtime.UnstructuredList{ - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_list", - }, + 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{ - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_list", - }, + Object: map[string]interface{}{"apiVersion": "test", "kind": "test_list"}, Items: []*runtime.Unstructured{ { - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_kind", - }, - Name: "object1", Object: map[string]interface{}{ "metadata": map[string]interface{}{"name": "object1"}, "apiVersion": "test", @@ -103,11 +97,6 @@ func TestDecode(t *testing.T) { }, }, { - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_kind", - }, - Name: "object2", Object: map[string]interface{}{ "metadata": map[string]interface{}{"name": "object2"}, "apiVersion": "test", @@ -132,6 +121,177 @@ func TestDecode(t *testing.T) { } } +func TestUnstructuredGetters(t *testing.T) { + unstruct := runtime.Unstructured{ + Object: map[string]interface{}{ + "kind": "test_kind", + "apiVersion": "test_version", + "metadata": map[string]interface{}{ + "name": "test_name", + "namespace": "test_namespace", + "generateName": "test_generateName", + "uid": "test_uid", + "resourceVersion": "test_resourceVersion", + "selfLink": "test_selfLink", + "creationTimestamp": "2009-11-10T23:00:00Z", + "deletionTimestamp": "2010-11-10T23:00:00Z", + "labels": map[string]interface{}{ + "test_label": "test_value", + }, + "annotations": map[string]interface{}{ + "test_annotation": "test_value", + }, + }, + }, + } + + if got, want := unstruct.GetAPIVersion(), "test_version"; got != want { + t.Errorf("GetAPIVersions() = %s, want %s", got, want) + } + + if got, want := unstruct.GetKind(), "test_kind"; got != want { + t.Errorf("GetKind() = %s, want %s", got, want) + } + + if got, want := unstruct.GetNamespace(), "test_namespace"; got != want { + t.Errorf("GetNamespace() = %s, want %s", got, want) + } + + if got, want := unstruct.GetName(), "test_name"; got != want { + t.Errorf("GetName() = %s, want %s", got, want) + } + + if got, want := unstruct.GetGenerateName(), "test_generateName"; got != want { + t.Errorf("GetGenerateName() = %s, want %s", got, want) + } + + if got, want := unstruct.GetUID(), types.UID("test_uid"); got != want { + t.Errorf("GetUID() = %s, want %s", got, want) + } + + if got, want := unstruct.GetResourceVersion(), "test_resourceVersion"; got != want { + t.Errorf("GetResourceVersion() = %s, want %s", got, want) + } + + if got, want := unstruct.GetSelfLink(), "test_selfLink"; got != want { + t.Errorf("GetSelfLink() = %s, want %s", got, want) + } + + if got, want := unstruct.GetCreationTimestamp(), unversioned.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC); !got.Equal(want) { + t.Errorf("GetCreationTimestamp() = %s, want %s", got, want) + } + + if got, want := unstruct.GetDeletionTimestamp(), unversioned.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC); got == nil || !got.Equal(want) { + t.Errorf("GetDeletionTimestamp() = %s, want %s", got, want) + } + + if got, want := unstruct.GetLabels(), map[string]string{"test_label": "test_value"}; !reflect.DeepEqual(got, want) { + t.Errorf("GetLabels() = %s, want %s", got, want) + } + + if got, want := unstruct.GetAnnotations(), map[string]string{"test_annotation": "test_value"}; !reflect.DeepEqual(got, want) { + t.Errorf("GetAnnotations() = %s, want %s", got, want) + } +} + +func TestUnstructuredSetters(t *testing.T) { + unstruct := runtime.Unstructured{} + + want := runtime.Unstructured{ + Object: map[string]interface{}{ + "kind": "test_kind", + "apiVersion": "test_version", + "metadata": map[string]interface{}{ + "name": "test_name", + "namespace": "test_namespace", + "generateName": "test_generateName", + "uid": "test_uid", + "resourceVersion": "test_resourceVersion", + "selfLink": "test_selfLink", + "creationTimestamp": "2009-11-10T23:00:00Z", + "deletionTimestamp": "2010-11-10T23:00:00Z", + "labels": map[string]interface{}{ + "test_label": "test_value", + }, + "annotations": map[string]interface{}{ + "test_annotation": "test_value", + }, + }, + }, + } + + unstruct.SetAPIVersion("test_version") + unstruct.SetKind("test_kind") + unstruct.SetNamespace("test_namespace") + unstruct.SetName("test_name") + unstruct.SetGenerateName("test_generateName") + unstruct.SetUID(types.UID("test_uid")) + unstruct.SetResourceVersion("test_resourceVersion") + unstruct.SetSelfLink("test_selfLink") + unstruct.SetCreationTimestamp(unversioned.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)) + date := unversioned.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC) + unstruct.SetDeletionTimestamp(&date) + unstruct.SetLabels(map[string]string{"test_label": "test_value"}) + unstruct.SetAnnotations(map[string]string{"test_annotation": "test_value"}) + + if !reflect.DeepEqual(unstruct, want) { + t.Errorf("Wanted: \n%s\n Got:\n%s", unstruct, want) + } +} + +func TestUnstructuredListGetters(t *testing.T) { + unstruct := runtime.UnstructuredList{ + Object: map[string]interface{}{ + "kind": "test_kind", + "apiVersion": "test_version", + "metadata": map[string]interface{}{ + "resourceVersion": "test_resourceVersion", + "selfLink": "test_selfLink", + }, + }, + } + + if got, want := unstruct.GetAPIVersion(), "test_version"; got != want { + t.Errorf("GetAPIVersions() = %s, want %s", got, want) + } + + if got, want := unstruct.GetKind(), "test_kind"; got != want { + t.Errorf("GetKind() = %s, want %s", got, want) + } + + if got, want := unstruct.GetResourceVersion(), "test_resourceVersion"; got != want { + t.Errorf("GetResourceVersion() = %s, want %s", got, want) + } + + if got, want := unstruct.GetSelfLink(), "test_selfLink"; got != want { + t.Errorf("GetSelfLink() = %s, want %s", got, want) + } +} + +func TestUnstructuredListSetters(t *testing.T) { + unstruct := runtime.UnstructuredList{} + + want := runtime.UnstructuredList{ + Object: map[string]interface{}{ + "kind": "test_kind", + "apiVersion": "test_version", + "metadata": map[string]interface{}{ + "resourceVersion": "test_resourceVersion", + "selfLink": "test_selfLink", + }, + }, + } + + unstruct.SetAPIVersion("test_version") + unstruct.SetKind("test_kind") + unstruct.SetResourceVersion("test_resourceVersion") + unstruct.SetSelfLink("test_selfLink") + + if !reflect.DeepEqual(unstruct, want) { + t.Errorf("Wanted: \n%s\n Got:\n%s", unstruct, want) + } +} + func TestDecodeNumbers(t *testing.T) { // Start with a valid pod