embedded_test.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. Copyright 2014 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. "encoding/json"
  16. "reflect"
  17. "strings"
  18. "testing"
  19. "k8s.io/kubernetes/pkg/api/meta"
  20. "k8s.io/kubernetes/pkg/api/unversioned"
  21. "k8s.io/kubernetes/pkg/runtime"
  22. "k8s.io/kubernetes/pkg/runtime/serializer"
  23. "k8s.io/kubernetes/pkg/util/diff"
  24. )
  25. type EmbeddedTest struct {
  26. runtime.TypeMeta
  27. ID string
  28. Object runtime.Object
  29. EmptyObject runtime.Object
  30. }
  31. type EmbeddedTestExternal struct {
  32. runtime.TypeMeta `json:",inline"`
  33. ID string `json:"id,omitempty"`
  34. Object runtime.RawExtension `json:"object,omitempty"`
  35. EmptyObject runtime.RawExtension `json:"emptyObject,omitempty"`
  36. }
  37. type ObjectTest struct {
  38. runtime.TypeMeta
  39. ID string
  40. Items []runtime.Object
  41. }
  42. type ObjectTestExternal struct {
  43. runtime.TypeMeta `yaml:",inline" json:",inline"`
  44. ID string `json:"id,omitempty"`
  45. Items []runtime.RawExtension `json:"items,omitempty"`
  46. }
  47. func (obj *ObjectTest) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  48. func (obj *ObjectTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  49. func (obj *EmbeddedTest) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  50. func (obj *EmbeddedTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  51. func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
  52. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  53. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
  54. externalGVK := externalGV.WithKind("ObjectTest")
  55. s := runtime.NewScheme()
  56. s.AddKnownTypes(internalGV, &ObjectTest{})
  57. s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{})
  58. codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
  59. obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil)
  60. if err != nil {
  61. t.Fatalf("unexpected error: %v", err)
  62. }
  63. test := obj.(*ObjectTest)
  64. if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != "{}" || unk.ContentType != runtime.ContentTypeJSON {
  65. t.Fatalf("unexpected object: %#v", test.Items[0])
  66. }
  67. if *gvk != externalGVK {
  68. t.Fatalf("unexpected kind: %#v", gvk)
  69. }
  70. obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil)
  71. if err != nil {
  72. t.Fatalf("unexpected error: %v", err)
  73. }
  74. test = obj.(*ObjectTest)
  75. if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != `{"kind":"Other","apiVersion":"v1"}` || unk.ContentType != runtime.ContentTypeJSON {
  76. t.Fatalf("unexpected object: %#v", test.Items[0])
  77. }
  78. if *gvk != externalGVK {
  79. t.Fatalf("unexpected kind: %#v", gvk)
  80. }
  81. }
  82. func TestArrayOfRuntimeObject(t *testing.T) {
  83. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  84. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
  85. s := runtime.NewScheme()
  86. s.AddKnownTypes(internalGV, &EmbeddedTest{})
  87. s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{})
  88. s.AddKnownTypes(internalGV, &ObjectTest{})
  89. s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{})
  90. codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
  91. innerItems := []runtime.Object{
  92. &EmbeddedTest{ID: "baz"},
  93. }
  94. items := []runtime.Object{
  95. &EmbeddedTest{ID: "foo"},
  96. &EmbeddedTest{ID: "bar"},
  97. // TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
  98. &runtime.Unknown{
  99. Raw: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`),
  100. ContentType: runtime.ContentTypeJSON,
  101. },
  102. &ObjectTest{
  103. Items: runtime.NewEncodableList(codec, innerItems),
  104. },
  105. }
  106. internal := &ObjectTest{
  107. Items: runtime.NewEncodableList(codec, items),
  108. }
  109. wire, err := runtime.Encode(codec, internal)
  110. if err != nil {
  111. t.Fatalf("unexpected error: %v", err)
  112. }
  113. t.Logf("Wire format is:\n%s\n", string(wire))
  114. obj := &ObjectTestExternal{}
  115. if err := json.Unmarshal(wire, obj); err != nil {
  116. t.Fatalf("unexpected error: %v", err)
  117. }
  118. t.Logf("exact wire is: %s", string(obj.Items[0].Raw))
  119. items[3] = &ObjectTest{Items: innerItems}
  120. internal.Items = items
  121. decoded, err := runtime.Decode(codec, wire)
  122. if err != nil {
  123. t.Fatalf("unexpected error: %v", err)
  124. }
  125. list, err := meta.ExtractList(decoded)
  126. if err != nil {
  127. t.Fatalf("unexpected error: %v", err)
  128. }
  129. if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
  130. t.Fatalf("unexpected error: %v", errs)
  131. }
  132. list2, err := meta.ExtractList(list[3])
  133. if err != nil {
  134. t.Fatalf("unexpected error: %v", err)
  135. }
  136. if errs := runtime.DecodeList(list2, codec); len(errs) > 0 {
  137. t.Fatalf("unexpected error: %v", errs)
  138. }
  139. if err := meta.SetList(list[3], list2); err != nil {
  140. t.Fatalf("unexpected error: %v", err)
  141. }
  142. // we want DecodeList to set type meta if possible, even on runtime.Unknown objects
  143. internal.Items[2].(*runtime.Unknown).TypeMeta = runtime.TypeMeta{Kind: "OtherTest", APIVersion: "unknown.group/unknown"}
  144. if e, a := internal.Items, list; !reflect.DeepEqual(e, a) {
  145. t.Errorf("mismatched decoded: %s", diff.ObjectGoPrintSideBySide(e, a))
  146. }
  147. }
  148. func TestNestedObject(t *testing.T) {
  149. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  150. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
  151. embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
  152. s := runtime.NewScheme()
  153. s.AddKnownTypes(internalGV, &EmbeddedTest{})
  154. s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
  155. codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
  156. inner := &EmbeddedTest{
  157. ID: "inner",
  158. }
  159. outer := &EmbeddedTest{
  160. ID: "outer",
  161. Object: runtime.NewEncodable(codec, inner),
  162. }
  163. wire, err := runtime.Encode(codec, outer)
  164. if err != nil {
  165. t.Fatalf("Unexpected encode error '%v'", err)
  166. }
  167. t.Logf("Wire format is:\n%v\n", string(wire))
  168. decoded, err := runtime.Decode(codec, wire)
  169. if err != nil {
  170. t.Fatalf("Unexpected decode error %v", err)
  171. }
  172. // for later tests
  173. outer.Object = inner
  174. if e, a := outer, decoded; reflect.DeepEqual(e, a) {
  175. t.Errorf("Expected unequal %#v %#v", e, a)
  176. }
  177. obj, err := runtime.Decode(codec, decoded.(*EmbeddedTest).Object.(*runtime.Unknown).Raw)
  178. if err != nil {
  179. t.Fatal(err)
  180. }
  181. decoded.(*EmbeddedTest).Object = obj
  182. if e, a := outer, decoded; !reflect.DeepEqual(e, a) {
  183. t.Errorf("Expected equal %#v %#v", e, a)
  184. }
  185. // test JSON decoding of the external object, which should preserve
  186. // raw bytes
  187. var externalViaJSON EmbeddedTestExternal
  188. err = json.Unmarshal(wire, &externalViaJSON)
  189. if err != nil {
  190. t.Fatalf("Unexpected decode error %v", err)
  191. }
  192. if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.ID != "outer" {
  193. t.Errorf("Expected objects to have type info set, got %#v", externalViaJSON)
  194. }
  195. if !reflect.DeepEqual(externalViaJSON.EmptyObject.Raw, []byte("null")) || len(externalViaJSON.Object.Raw) == 0 {
  196. t.Errorf("Expected deserialization of nested objects into bytes, got %#v", externalViaJSON)
  197. }
  198. // test JSON decoding, too, since Decode uses yaml unmarshalling.
  199. // Generic Unmarshalling of JSON cannot load the nested objects because there is
  200. // no default schema set. Consumers wishing to get direct JSON decoding must use
  201. // the external representation
  202. var decodedViaJSON EmbeddedTest
  203. err = json.Unmarshal(wire, &decodedViaJSON)
  204. if err == nil || !strings.Contains(err.Error(), "unmarshal object into Go value of type runtime.Object") {
  205. t.Fatalf("Unexpected decode error %v", err)
  206. }
  207. if a := decodedViaJSON; a.Object != nil || a.EmptyObject != nil {
  208. t.Errorf("Expected embedded objects to be nil: %#v", a)
  209. }
  210. }
  211. // TestDeepCopyOfRuntimeObject checks to make sure that runtime.Objects's can be passed through DeepCopy with fidelity
  212. func TestDeepCopyOfRuntimeObject(t *testing.T) {
  213. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  214. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
  215. embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
  216. s := runtime.NewScheme()
  217. s.AddKnownTypes(internalGV, &EmbeddedTest{})
  218. s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
  219. original := &EmbeddedTest{
  220. ID: "outer",
  221. Object: &EmbeddedTest{
  222. ID: "inner",
  223. },
  224. }
  225. codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
  226. originalData, err := runtime.Encode(codec, original)
  227. if err != nil {
  228. t.Errorf("unexpected error: %v", err)
  229. }
  230. t.Logf("originalRole = %v\n", string(originalData))
  231. copyOfOriginal, err := s.DeepCopy(original)
  232. if err != nil {
  233. t.Fatalf("unexpected error: %v", err)
  234. }
  235. copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object))
  236. if err != nil {
  237. t.Errorf("unexpected error: %v", err)
  238. }
  239. t.Logf("copyOfRole = %v\n", string(copiedData))
  240. if !reflect.DeepEqual(original, copyOfOriginal) {
  241. t.Errorf("expected \n%v\n, got \n%v", string(originalData), string(copiedData))
  242. }
  243. }