protobuf_test.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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 protobuf_test
  14. import (
  15. "bytes"
  16. "encoding/hex"
  17. "fmt"
  18. "reflect"
  19. "strings"
  20. "testing"
  21. "k8s.io/kubernetes/pkg/api"
  22. _ "k8s.io/kubernetes/pkg/api/install"
  23. "k8s.io/kubernetes/pkg/api/unversioned"
  24. "k8s.io/kubernetes/pkg/api/v1"
  25. "k8s.io/kubernetes/pkg/runtime"
  26. "k8s.io/kubernetes/pkg/runtime/serializer/protobuf"
  27. "k8s.io/kubernetes/pkg/util/diff"
  28. )
  29. type testObject struct {
  30. gvk unversioned.GroupVersionKind
  31. }
  32. func (d *testObject) GetObjectKind() unversioned.ObjectKind { return d }
  33. func (d *testObject) SetGroupVersionKind(gvk unversioned.GroupVersionKind) { d.gvk = gvk }
  34. func (d *testObject) GroupVersionKind() unversioned.GroupVersionKind { return d.gvk }
  35. type testMarshalable struct {
  36. testObject
  37. data []byte
  38. err error
  39. }
  40. func (d *testMarshalable) Marshal() ([]byte, error) {
  41. return d.data, d.err
  42. }
  43. type testBufferedMarshalable struct {
  44. testObject
  45. data []byte
  46. err error
  47. }
  48. func (d *testBufferedMarshalable) Marshal() ([]byte, error) {
  49. return nil, fmt.Errorf("not invokable")
  50. }
  51. func (d *testBufferedMarshalable) MarshalTo(data []byte) (int, error) {
  52. copy(data, d.data)
  53. return len(d.data), d.err
  54. }
  55. func (d *testBufferedMarshalable) Size() int {
  56. return len(d.data)
  57. }
  58. func TestRecognize(t *testing.T) {
  59. s := protobuf.NewSerializer(nil, nil, "application/protobuf")
  60. ignores := [][]byte{
  61. nil,
  62. {},
  63. []byte("k8s"),
  64. {0x6b, 0x38, 0x73, 0x01},
  65. }
  66. for i, data := range ignores {
  67. if ok, _, err := s.RecognizesData(bytes.NewBuffer(data)); err != nil || ok {
  68. t.Errorf("%d: should not recognize data: %v", i, err)
  69. }
  70. }
  71. recognizes := [][]byte{
  72. {0x6b, 0x38, 0x73, 0x00},
  73. {0x6b, 0x38, 0x73, 0x00, 0x01},
  74. }
  75. for i, data := range recognizes {
  76. if ok, _, err := s.RecognizesData(bytes.NewBuffer(data)); err != nil || !ok {
  77. t.Errorf("%d: should recognize data: %v", i, err)
  78. }
  79. }
  80. }
  81. func TestEncode(t *testing.T) {
  82. obj1 := &testMarshalable{testObject: testObject{}, data: []byte{}}
  83. wire1 := []byte{
  84. 0x6b, 0x38, 0x73, 0x00, // prefix
  85. 0x0a, 0x04,
  86. 0x0a, 0x00, // apiversion
  87. 0x12, 0x00, // kind
  88. 0x12, 0x00, // data
  89. 0x1a, 0x00, // content-type
  90. 0x22, 0x00, // content-encoding
  91. }
  92. obj2 := &testMarshalable{
  93. testObject: testObject{gvk: unversioned.GroupVersionKind{Kind: "test", Group: "other", Version: "version"}},
  94. data: []byte{0x01, 0x02, 0x03},
  95. }
  96. wire2 := []byte{
  97. 0x6b, 0x38, 0x73, 0x00, // prefix
  98. 0x0a, 0x15,
  99. 0x0a, 0x0d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // apiversion
  100. 0x12, 0x04, 0x74, 0x65, 0x73, 0x74, // kind
  101. 0x12, 0x03, 0x01, 0x02, 0x03, // data
  102. 0x1a, 0x00, // content-type
  103. 0x22, 0x00, // content-encoding
  104. }
  105. err1 := fmt.Errorf("a test error")
  106. testCases := []struct {
  107. obj runtime.Object
  108. data []byte
  109. errFn func(error) bool
  110. }{
  111. {
  112. obj: &testObject{},
  113. errFn: protobuf.IsNotMarshalable,
  114. },
  115. {
  116. obj: obj1,
  117. data: wire1,
  118. },
  119. {
  120. obj: &testMarshalable{testObject: obj1.testObject, err: err1},
  121. errFn: func(err error) bool { return err == err1 },
  122. },
  123. {
  124. // if this test fails, writing the "fast path" marshal is not the same as the "slow path"
  125. obj: &testBufferedMarshalable{testObject: obj1.testObject, data: obj1.data},
  126. data: wire1,
  127. },
  128. {
  129. obj: obj2,
  130. data: wire2,
  131. },
  132. {
  133. // if this test fails, writing the "fast path" marshal is not the same as the "slow path"
  134. obj: &testBufferedMarshalable{testObject: obj2.testObject, data: obj2.data},
  135. data: wire2,
  136. },
  137. {
  138. obj: &testBufferedMarshalable{testObject: obj1.testObject, err: err1},
  139. errFn: func(err error) bool { return err == err1 },
  140. },
  141. }
  142. for i, test := range testCases {
  143. s := protobuf.NewSerializer(nil, nil, "application/protobuf")
  144. data, err := runtime.Encode(s, test.obj)
  145. switch {
  146. case err == nil && test.errFn != nil:
  147. t.Errorf("%d: failed: %v", i, err)
  148. continue
  149. case err != nil && test.errFn == nil:
  150. t.Errorf("%d: failed: %v", i, err)
  151. continue
  152. case err != nil:
  153. if !test.errFn(err) {
  154. t.Errorf("%d: failed: %v", i, err)
  155. }
  156. if data != nil {
  157. t.Errorf("%d: should not have returned nil data", i)
  158. }
  159. continue
  160. }
  161. if test.data != nil && !bytes.Equal(test.data, data) {
  162. t.Errorf("%d: unexpected data:\n%s", i, hex.Dump(data))
  163. continue
  164. }
  165. if ok, _, err := s.RecognizesData(bytes.NewBuffer(data)); !ok || err != nil {
  166. t.Errorf("%d: did not recognize data generated by call: %v", i, err)
  167. }
  168. }
  169. }
  170. func TestDecode(t *testing.T) {
  171. wire1 := []byte{
  172. 0x6b, 0x38, 0x73, 0x00, // prefix
  173. 0x0a, 0x04,
  174. 0x0a, 0x00, // apiversion
  175. 0x12, 0x00, // kind
  176. 0x12, 0x00, // data
  177. 0x1a, 0x00, // content-type
  178. 0x22, 0x00, // content-encoding
  179. }
  180. wire2 := []byte{
  181. 0x6b, 0x38, 0x73, 0x00, // prefix
  182. 0x0a, 0x15,
  183. 0x0a, 0x0d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // apiversion
  184. 0x12, 0x04, 0x74, 0x65, 0x73, 0x74, // kind
  185. 0x12, 0x07, 0x6b, 0x38, 0x73, 0x00, 0x01, 0x02, 0x03, // data
  186. 0x1a, 0x00, // content-type
  187. 0x22, 0x00, // content-encoding
  188. }
  189. //err1 := fmt.Errorf("a test error")
  190. testCases := []struct {
  191. obj runtime.Object
  192. data []byte
  193. errFn func(error) bool
  194. }{
  195. {
  196. obj: &runtime.Unknown{},
  197. errFn: func(err error) bool { return err.Error() == "empty data" },
  198. },
  199. {
  200. data: []byte{0x6b},
  201. errFn: func(err error) bool { return strings.Contains(err.Error(), "does not appear to be a protobuf message") },
  202. },
  203. {
  204. obj: &runtime.Unknown{
  205. Raw: []byte{},
  206. },
  207. data: wire1,
  208. },
  209. {
  210. obj: &runtime.Unknown{
  211. TypeMeta: runtime.TypeMeta{
  212. APIVersion: "other/version",
  213. Kind: "test",
  214. },
  215. // content type is set because the prefix matches the content
  216. ContentType: "application/protobuf",
  217. Raw: []byte{0x6b, 0x38, 0x73, 0x00, 0x01, 0x02, 0x03},
  218. },
  219. data: wire2,
  220. },
  221. }
  222. for i, test := range testCases {
  223. s := protobuf.NewSerializer(nil, nil, "application/protobuf")
  224. unk := &runtime.Unknown{}
  225. err := runtime.DecodeInto(s, test.data, unk)
  226. switch {
  227. case err == nil && test.errFn != nil:
  228. t.Errorf("%d: failed: %v", i, err)
  229. continue
  230. case err != nil && test.errFn == nil:
  231. t.Errorf("%d: failed: %v", i, err)
  232. continue
  233. case err != nil:
  234. if !test.errFn(err) {
  235. t.Errorf("%d: failed: %v", i, err)
  236. }
  237. continue
  238. }
  239. if !reflect.DeepEqual(unk, test.obj) {
  240. t.Errorf("%d: unexpected object:\n%#v", i, unk)
  241. continue
  242. }
  243. }
  244. }
  245. func TestDecodeObjects(t *testing.T) {
  246. obj1 := &v1.Pod{
  247. ObjectMeta: v1.ObjectMeta{
  248. Name: "cool",
  249. },
  250. Spec: v1.PodSpec{
  251. Containers: []v1.Container{
  252. {
  253. Name: "test",
  254. },
  255. },
  256. },
  257. }
  258. obj1wire, err := obj1.Marshal()
  259. if err != nil {
  260. t.Fatal(err)
  261. }
  262. wire1, err := (&runtime.Unknown{
  263. TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: "v1"},
  264. Raw: obj1wire,
  265. }).Marshal()
  266. if err != nil {
  267. t.Fatal(err)
  268. }
  269. unk2 := &runtime.Unknown{
  270. TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: "v1"},
  271. }
  272. wire2 := make([]byte, len(wire1)*2)
  273. n, err := unk2.NestedMarshalTo(wire2, obj1, uint64(obj1.Size()))
  274. if err != nil {
  275. t.Fatal(err)
  276. }
  277. if n != len(wire1) || !bytes.Equal(wire1, wire2[:n]) {
  278. t.Fatalf("unexpected wire:\n%s", hex.Dump(wire2[:n]))
  279. }
  280. wire1 = append([]byte{0x6b, 0x38, 0x73, 0x00}, wire1...)
  281. testCases := []struct {
  282. obj runtime.Object
  283. data []byte
  284. errFn func(error) bool
  285. }{
  286. {
  287. obj: obj1,
  288. data: wire1,
  289. },
  290. }
  291. for i, test := range testCases {
  292. s := protobuf.NewSerializer(api.Scheme, api.Scheme, "application/protobuf")
  293. obj, err := runtime.Decode(s, test.data)
  294. switch {
  295. case err == nil && test.errFn != nil:
  296. t.Errorf("%d: failed: %v", i, err)
  297. continue
  298. case err != nil && test.errFn == nil:
  299. t.Errorf("%d: failed: %v", i, err)
  300. continue
  301. case err != nil:
  302. if !test.errFn(err) {
  303. t.Errorf("%d: failed: %v", i, err)
  304. }
  305. if obj != nil {
  306. t.Errorf("%d: should not have returned an object", i)
  307. }
  308. continue
  309. }
  310. if !api.Semantic.DeepEqual(obj, test.obj) {
  311. t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintDiff(test.obj, obj))
  312. continue
  313. }
  314. }
  315. }