unstructured_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package runtime_test
  14. import (
  15. "fmt"
  16. "reflect"
  17. "strings"
  18. "testing"
  19. "time"
  20. "k8s.io/kubernetes/pkg/api"
  21. "k8s.io/kubernetes/pkg/api/meta/metatypes"
  22. "k8s.io/kubernetes/pkg/api/testapi"
  23. "k8s.io/kubernetes/pkg/api/unversioned"
  24. "k8s.io/kubernetes/pkg/api/validation"
  25. "k8s.io/kubernetes/pkg/runtime"
  26. "k8s.io/kubernetes/pkg/types"
  27. )
  28. func TestDecodeUnstructured(t *testing.T) {
  29. groupVersionString := testapi.Default.GroupVersion().String()
  30. rawJson := fmt.Sprintf(`{"kind":"Pod","apiVersion":"%s","metadata":{"name":"test"}}`, groupVersionString)
  31. pl := &api.List{
  32. Items: []runtime.Object{
  33. &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
  34. &runtime.Unknown{
  35. TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: groupVersionString},
  36. Raw: []byte(rawJson),
  37. ContentType: runtime.ContentTypeJSON,
  38. },
  39. &runtime.Unknown{
  40. TypeMeta: runtime.TypeMeta{Kind: "", APIVersion: groupVersionString},
  41. Raw: []byte(rawJson),
  42. ContentType: runtime.ContentTypeJSON,
  43. },
  44. &runtime.Unstructured{
  45. Object: map[string]interface{}{
  46. "kind": "Foo",
  47. "apiVersion": "Bar",
  48. "test": "value",
  49. },
  50. },
  51. },
  52. }
  53. if errs := runtime.DecodeList(pl.Items, runtime.UnstructuredJSONScheme); len(errs) == 1 {
  54. t.Fatalf("unexpected error %v", errs)
  55. }
  56. if pod, ok := pl.Items[1].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
  57. t.Errorf("object not converted: %#v", pl.Items[1])
  58. }
  59. if pod, ok := pl.Items[2].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
  60. t.Errorf("object not converted: %#v", pl.Items[2])
  61. }
  62. }
  63. func TestDecode(t *testing.T) {
  64. tcs := []struct {
  65. json []byte
  66. want runtime.Object
  67. }{
  68. {
  69. json: []byte(`{"apiVersion": "test", "kind": "test_kind"}`),
  70. want: &runtime.Unstructured{
  71. Object: map[string]interface{}{"apiVersion": "test", "kind": "test_kind"},
  72. },
  73. },
  74. {
  75. json: []byte(`{"apiVersion": "test", "kind": "test_list", "items": []}`),
  76. want: &runtime.UnstructuredList{
  77. Object: map[string]interface{}{"apiVersion": "test", "kind": "test_list"},
  78. },
  79. },
  80. {
  81. json: []byte(`{"items": [{"metadata": {"name": "object1"}, "apiVersion": "test", "kind": "test_kind"}, {"metadata": {"name": "object2"}, "apiVersion": "test", "kind": "test_kind"}], "apiVersion": "test", "kind": "test_list"}`),
  82. want: &runtime.UnstructuredList{
  83. Object: map[string]interface{}{"apiVersion": "test", "kind": "test_list"},
  84. Items: []*runtime.Unstructured{
  85. {
  86. Object: map[string]interface{}{
  87. "metadata": map[string]interface{}{"name": "object1"},
  88. "apiVersion": "test",
  89. "kind": "test_kind",
  90. },
  91. },
  92. {
  93. Object: map[string]interface{}{
  94. "metadata": map[string]interface{}{"name": "object2"},
  95. "apiVersion": "test",
  96. "kind": "test_kind",
  97. },
  98. },
  99. },
  100. },
  101. },
  102. }
  103. for _, tc := range tcs {
  104. got, _, err := runtime.UnstructuredJSONScheme.Decode(tc.json, nil, nil)
  105. if err != nil {
  106. t.Errorf("Unexpected error for %q: %v", string(tc.json), err)
  107. continue
  108. }
  109. if !reflect.DeepEqual(got, tc.want) {
  110. t.Errorf("Decode(%q) want: %v\ngot: %v", string(tc.json), tc.want, got)
  111. }
  112. }
  113. }
  114. func TestUnstructuredGetters(t *testing.T) {
  115. unstruct := runtime.Unstructured{
  116. Object: map[string]interface{}{
  117. "kind": "test_kind",
  118. "apiVersion": "test_version",
  119. "metadata": map[string]interface{}{
  120. "name": "test_name",
  121. "namespace": "test_namespace",
  122. "generateName": "test_generateName",
  123. "uid": "test_uid",
  124. "resourceVersion": "test_resourceVersion",
  125. "selfLink": "test_selfLink",
  126. "creationTimestamp": "2009-11-10T23:00:00Z",
  127. "deletionTimestamp": "2010-11-10T23:00:00Z",
  128. "labels": map[string]interface{}{
  129. "test_label": "test_value",
  130. },
  131. "annotations": map[string]interface{}{
  132. "test_annotation": "test_value",
  133. },
  134. "ownerReferences": []map[string]interface{}{
  135. {
  136. "kind": "Pod",
  137. "name": "poda",
  138. "apiVersion": "v1",
  139. "uid": "1",
  140. },
  141. {
  142. "kind": "Pod",
  143. "name": "podb",
  144. "apiVersion": "v1",
  145. "uid": "2",
  146. },
  147. },
  148. "finalizers": []interface{}{
  149. "finalizer.1",
  150. "finalizer.2",
  151. },
  152. "clusterName": "cluster123",
  153. },
  154. },
  155. }
  156. if got, want := unstruct.GetAPIVersion(), "test_version"; got != want {
  157. t.Errorf("GetAPIVersions() = %s, want %s", got, want)
  158. }
  159. if got, want := unstruct.GetKind(), "test_kind"; got != want {
  160. t.Errorf("GetKind() = %s, want %s", got, want)
  161. }
  162. if got, want := unstruct.GetNamespace(), "test_namespace"; got != want {
  163. t.Errorf("GetNamespace() = %s, want %s", got, want)
  164. }
  165. if got, want := unstruct.GetName(), "test_name"; got != want {
  166. t.Errorf("GetName() = %s, want %s", got, want)
  167. }
  168. if got, want := unstruct.GetGenerateName(), "test_generateName"; got != want {
  169. t.Errorf("GetGenerateName() = %s, want %s", got, want)
  170. }
  171. if got, want := unstruct.GetUID(), types.UID("test_uid"); got != want {
  172. t.Errorf("GetUID() = %s, want %s", got, want)
  173. }
  174. if got, want := unstruct.GetResourceVersion(), "test_resourceVersion"; got != want {
  175. t.Errorf("GetResourceVersion() = %s, want %s", got, want)
  176. }
  177. if got, want := unstruct.GetSelfLink(), "test_selfLink"; got != want {
  178. t.Errorf("GetSelfLink() = %s, want %s", got, want)
  179. }
  180. if got, want := unstruct.GetCreationTimestamp(), unversioned.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC); !got.Equal(want) {
  181. t.Errorf("GetCreationTimestamp() = %s, want %s", got, want)
  182. }
  183. if got, want := unstruct.GetDeletionTimestamp(), unversioned.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC); got == nil || !got.Equal(want) {
  184. t.Errorf("GetDeletionTimestamp() = %s, want %s", got, want)
  185. }
  186. if got, want := unstruct.GetLabels(), map[string]string{"test_label": "test_value"}; !reflect.DeepEqual(got, want) {
  187. t.Errorf("GetLabels() = %s, want %s", got, want)
  188. }
  189. if got, want := unstruct.GetAnnotations(), map[string]string{"test_annotation": "test_value"}; !reflect.DeepEqual(got, want) {
  190. t.Errorf("GetAnnotations() = %s, want %s", got, want)
  191. }
  192. refs := unstruct.GetOwnerReferences()
  193. expectedOwnerReferences := []metatypes.OwnerReference{
  194. {
  195. Kind: "Pod",
  196. Name: "poda",
  197. APIVersion: "v1",
  198. UID: "1",
  199. },
  200. {
  201. Kind: "Pod",
  202. Name: "podb",
  203. APIVersion: "v1",
  204. UID: "2",
  205. },
  206. }
  207. if got, want := refs, expectedOwnerReferences; !reflect.DeepEqual(got, want) {
  208. t.Errorf("GetOwnerReferences()=%v, want %v", got, want)
  209. }
  210. if got, want := unstruct.GetFinalizers(), []string{"finalizer.1", "finalizer.2"}; !reflect.DeepEqual(got, want) {
  211. t.Errorf("GetFinalizers()=%v, want %v", got, want)
  212. }
  213. if got, want := unstruct.GetClusterName(), "cluster123"; got != want {
  214. t.Errorf("GetClusterName()=%v, want %v", got, want)
  215. }
  216. }
  217. func TestUnstructuredSetters(t *testing.T) {
  218. unstruct := runtime.Unstructured{}
  219. trueVar := true
  220. want := runtime.Unstructured{
  221. Object: map[string]interface{}{
  222. "kind": "test_kind",
  223. "apiVersion": "test_version",
  224. "metadata": map[string]interface{}{
  225. "name": "test_name",
  226. "namespace": "test_namespace",
  227. "generateName": "test_generateName",
  228. "uid": "test_uid",
  229. "resourceVersion": "test_resourceVersion",
  230. "selfLink": "test_selfLink",
  231. "creationTimestamp": "2009-11-10T23:00:00Z",
  232. "deletionTimestamp": "2010-11-10T23:00:00Z",
  233. "labels": map[string]interface{}{
  234. "test_label": "test_value",
  235. },
  236. "annotations": map[string]interface{}{
  237. "test_annotation": "test_value",
  238. },
  239. "ownerReferences": []map[string]interface{}{
  240. {
  241. "kind": "Pod",
  242. "name": "poda",
  243. "apiVersion": "v1",
  244. "uid": "1",
  245. "controller": (*bool)(nil),
  246. },
  247. {
  248. "kind": "Pod",
  249. "name": "podb",
  250. "apiVersion": "v1",
  251. "uid": "2",
  252. "controller": &trueVar,
  253. },
  254. },
  255. "finalizers": []interface{}{
  256. "finalizer.1",
  257. "finalizer.2",
  258. },
  259. "clusterName": "cluster123",
  260. },
  261. },
  262. }
  263. unstruct.SetAPIVersion("test_version")
  264. unstruct.SetKind("test_kind")
  265. unstruct.SetNamespace("test_namespace")
  266. unstruct.SetName("test_name")
  267. unstruct.SetGenerateName("test_generateName")
  268. unstruct.SetUID(types.UID("test_uid"))
  269. unstruct.SetResourceVersion("test_resourceVersion")
  270. unstruct.SetSelfLink("test_selfLink")
  271. unstruct.SetCreationTimestamp(unversioned.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC))
  272. date := unversioned.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC)
  273. unstruct.SetDeletionTimestamp(&date)
  274. unstruct.SetLabels(map[string]string{"test_label": "test_value"})
  275. unstruct.SetAnnotations(map[string]string{"test_annotation": "test_value"})
  276. newOwnerReferences := []metatypes.OwnerReference{
  277. {
  278. Kind: "Pod",
  279. Name: "poda",
  280. APIVersion: "v1",
  281. UID: "1",
  282. },
  283. {
  284. Kind: "Pod",
  285. Name: "podb",
  286. APIVersion: "v1",
  287. UID: "2",
  288. Controller: &trueVar,
  289. },
  290. }
  291. unstruct.SetOwnerReferences(newOwnerReferences)
  292. unstruct.SetFinalizers([]string{"finalizer.1", "finalizer.2"})
  293. unstruct.SetClusterName("cluster123")
  294. if !reflect.DeepEqual(unstruct, want) {
  295. t.Errorf("Wanted: \n%s\n Got:\n%s", want, unstruct)
  296. }
  297. }
  298. func TestUnstructuredListGetters(t *testing.T) {
  299. unstruct := runtime.UnstructuredList{
  300. Object: map[string]interface{}{
  301. "kind": "test_kind",
  302. "apiVersion": "test_version",
  303. "metadata": map[string]interface{}{
  304. "resourceVersion": "test_resourceVersion",
  305. "selfLink": "test_selfLink",
  306. },
  307. },
  308. }
  309. if got, want := unstruct.GetAPIVersion(), "test_version"; got != want {
  310. t.Errorf("GetAPIVersions() = %s, want %s", got, want)
  311. }
  312. if got, want := unstruct.GetKind(), "test_kind"; got != want {
  313. t.Errorf("GetKind() = %s, want %s", got, want)
  314. }
  315. if got, want := unstruct.GetResourceVersion(), "test_resourceVersion"; got != want {
  316. t.Errorf("GetResourceVersion() = %s, want %s", got, want)
  317. }
  318. if got, want := unstruct.GetSelfLink(), "test_selfLink"; got != want {
  319. t.Errorf("GetSelfLink() = %s, want %s", got, want)
  320. }
  321. }
  322. func TestUnstructuredListSetters(t *testing.T) {
  323. unstruct := runtime.UnstructuredList{}
  324. want := runtime.UnstructuredList{
  325. Object: map[string]interface{}{
  326. "kind": "test_kind",
  327. "apiVersion": "test_version",
  328. "metadata": map[string]interface{}{
  329. "resourceVersion": "test_resourceVersion",
  330. "selfLink": "test_selfLink",
  331. },
  332. },
  333. }
  334. unstruct.SetAPIVersion("test_version")
  335. unstruct.SetKind("test_kind")
  336. unstruct.SetResourceVersion("test_resourceVersion")
  337. unstruct.SetSelfLink("test_selfLink")
  338. if !reflect.DeepEqual(unstruct, want) {
  339. t.Errorf("Wanted: \n%s\n Got:\n%s", unstruct, want)
  340. }
  341. }
  342. func TestDecodeNumbers(t *testing.T) {
  343. // Start with a valid pod
  344. originalJSON := []byte(`{
  345. "kind":"Pod",
  346. "apiVersion":"v1",
  347. "metadata":{"name":"pod","namespace":"foo"},
  348. "spec":{
  349. "containers":[{"name":"container","image":"container"}],
  350. "activeDeadlineSeconds":1000030003
  351. }
  352. }`)
  353. pod := &api.Pod{}
  354. // Decode with structured codec
  355. codec, err := testapi.GetCodecForObject(pod)
  356. if err != nil {
  357. t.Fatalf("unexpected error: %v", err)
  358. }
  359. err = runtime.DecodeInto(codec, originalJSON, pod)
  360. if err != nil {
  361. t.Fatalf("unexpected error: %v", err)
  362. }
  363. // ensure pod is valid
  364. if errs := validation.ValidatePod(pod); len(errs) > 0 {
  365. t.Fatalf("pod should be valid: %v", errs)
  366. }
  367. // Round-trip with unstructured codec
  368. unstructuredObj, err := runtime.Decode(runtime.UnstructuredJSONScheme, originalJSON)
  369. if err != nil {
  370. t.Fatalf("unexpected error: %v", err)
  371. }
  372. roundtripJSON, err := runtime.Encode(runtime.UnstructuredJSONScheme, unstructuredObj)
  373. if err != nil {
  374. t.Fatalf("unexpected error: %v", err)
  375. }
  376. // Make sure we serialize back out in int form
  377. if !strings.Contains(string(roundtripJSON), `"activeDeadlineSeconds":1000030003`) {
  378. t.Errorf("Expected %s, got %s", `"activeDeadlineSeconds":1000030003`, string(roundtripJSON))
  379. }
  380. // Decode with structured codec again
  381. obj2, err := runtime.Decode(codec, roundtripJSON)
  382. if err != nil {
  383. t.Fatalf("unexpected error: %v", err)
  384. }
  385. // ensure pod is still valid
  386. pod2, ok := obj2.(*api.Pod)
  387. if !ok {
  388. t.Fatalf("expected an *api.Pod, got %#v", obj2)
  389. }
  390. if errs := validation.ValidatePod(pod2); len(errs) > 0 {
  391. t.Fatalf("pod should be valid: %v", errs)
  392. }
  393. // ensure round-trip preserved large integers
  394. if !reflect.DeepEqual(pod, pod2) {
  395. t.Fatalf("Expected\n\t%#v, got \n\t%#v", pod, pod2)
  396. }
  397. }