serialization_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  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 testing
  14. import (
  15. "bytes"
  16. "encoding/hex"
  17. gojson "encoding/json"
  18. "io/ioutil"
  19. "math/rand"
  20. "reflect"
  21. "testing"
  22. jsoniter "github.com/json-iterator/go"
  23. appsv1 "k8s.io/api/apps/v1"
  24. v1 "k8s.io/api/core/v1"
  25. "k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
  26. "k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
  27. apiequality "k8s.io/apimachinery/pkg/api/equality"
  28. "k8s.io/apimachinery/pkg/api/meta"
  29. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  30. "k8s.io/apimachinery/pkg/conversion"
  31. "k8s.io/apimachinery/pkg/runtime"
  32. "k8s.io/apimachinery/pkg/runtime/schema"
  33. "k8s.io/apimachinery/pkg/runtime/serializer/json"
  34. "k8s.io/apimachinery/pkg/runtime/serializer/streaming"
  35. "k8s.io/apimachinery/pkg/util/diff"
  36. "k8s.io/apimachinery/pkg/util/sets"
  37. "k8s.io/apimachinery/pkg/watch"
  38. "k8s.io/kubernetes/pkg/api/legacyscheme"
  39. "k8s.io/kubernetes/pkg/apis/apps"
  40. k8s_apps_v1 "k8s.io/kubernetes/pkg/apis/apps/v1"
  41. api "k8s.io/kubernetes/pkg/apis/core"
  42. k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
  43. "sigs.k8s.io/yaml"
  44. )
  45. // fuzzInternalObject fuzzes an arbitrary runtime object using the appropriate
  46. // fuzzer registered with the apitesting package.
  47. func fuzzInternalObject(t *testing.T, forVersion schema.GroupVersion, item runtime.Object, seed int64) runtime.Object {
  48. fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(seed), legacyscheme.Codecs).Fuzz(item)
  49. j, err := meta.TypeAccessor(item)
  50. if err != nil {
  51. t.Fatalf("Unexpected error %v for %#v", err, item)
  52. }
  53. j.SetKind("")
  54. j.SetAPIVersion("")
  55. return item
  56. }
  57. func ConvertV1ReplicaSetToAPIReplicationController(in *appsv1.ReplicaSet, out *api.ReplicationController, s conversion.Scope) error {
  58. intermediate1 := &apps.ReplicaSet{}
  59. if err := k8s_apps_v1.Convert_v1_ReplicaSet_To_apps_ReplicaSet(in, intermediate1, s); err != nil {
  60. return err
  61. }
  62. intermediate2 := &v1.ReplicationController{}
  63. if err := k8s_api_v1.Convert_apps_ReplicaSet_To_v1_ReplicationController(intermediate1, intermediate2, s); err != nil {
  64. return err
  65. }
  66. return k8s_api_v1.Convert_v1_ReplicationController_To_core_ReplicationController(intermediate2, out, s)
  67. }
  68. func TestSetControllerConversion(t *testing.T) {
  69. s := legacyscheme.Scheme
  70. if err := s.AddConversionFunc((*appsv1.ReplicaSet)(nil), (*api.ReplicationController)(nil), func(a, b interface{}, scope conversion.Scope) error {
  71. return ConvertV1ReplicaSetToAPIReplicationController(a.(*appsv1.ReplicaSet), b.(*api.ReplicationController), scope)
  72. }); err != nil {
  73. t.Fatal(err)
  74. }
  75. rs := &apps.ReplicaSet{}
  76. rc := &api.ReplicationController{}
  77. extGroup := schema.GroupVersion{Group: "apps", Version: "v1"}
  78. extCodec := legacyscheme.Codecs.LegacyCodec(extGroup)
  79. defaultGroup := schema.GroupVersion{Group: "", Version: "v1"}
  80. defaultCodec := legacyscheme.Codecs.LegacyCodec(defaultGroup)
  81. fuzzInternalObject(t, schema.GroupVersion{Group: "apps", Version: runtime.APIVersionInternal}, rs, rand.Int63())
  82. // explicitly set the selector to something that is convertible to old-style selectors
  83. // (since normally we'll fuzz the selectors with things that aren't convertible)
  84. rs.Spec.Selector = &metav1.LabelSelector{
  85. MatchLabels: map[string]string{
  86. "foo": "bar",
  87. "baz": "quux",
  88. },
  89. }
  90. t.Logf("rs._internal.apps -> rs.v1.apps")
  91. data, err := runtime.Encode(extCodec, rs)
  92. if err != nil {
  93. t.Fatalf("unexpected encoding error: %v", err)
  94. }
  95. decoder := legacyscheme.Codecs.DecoderToVersion(
  96. legacyscheme.Codecs.UniversalDeserializer(),
  97. runtime.NewMultiGroupVersioner(
  98. defaultGroup,
  99. schema.GroupKind{Group: defaultGroup.Group},
  100. schema.GroupKind{Group: extGroup.Group},
  101. ),
  102. )
  103. t.Logf("rs.v1.apps -> rc._internal")
  104. if err := runtime.DecodeInto(decoder, data, rc); err != nil {
  105. t.Fatalf("unexpected decoding error: %v", err)
  106. }
  107. t.Logf("rc._internal -> rc.v1")
  108. data, err = runtime.Encode(defaultCodec, rc)
  109. if err != nil {
  110. t.Fatalf("unexpected encoding error: %v", err)
  111. }
  112. t.Logf("rc.v1 -> rs._internal.apps")
  113. if err := runtime.DecodeInto(decoder, data, rs); err != nil {
  114. t.Fatalf("unexpected decoding error: %v", err)
  115. }
  116. }
  117. // TestSpecificKind round-trips a single specific kind and is intended to help
  118. // debug issues that arise while adding a new API type.
  119. func TestSpecificKind(t *testing.T) {
  120. // Uncomment the following line to enable logging of which conversions
  121. // legacyscheme.Scheme.Log(t)
  122. internalGVK := schema.GroupVersionKind{Group: "apps", Version: runtime.APIVersionInternal, Kind: "DaemonSet"}
  123. seed := rand.Int63()
  124. fuzzer := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(seed), legacyscheme.Codecs)
  125. roundtrip.RoundTripSpecificKind(t, internalGVK, legacyscheme.Scheme, legacyscheme.Codecs, fuzzer, nil)
  126. }
  127. var nonRoundTrippableTypes = sets.NewString(
  128. "ExportOptions",
  129. "GetOptions",
  130. // WatchEvent does not include kind and version and can only be deserialized
  131. // implicitly (if the caller expects the specific object). The watch call defines
  132. // the schema by content type, rather than via kind/version included in each
  133. // object.
  134. "WatchEvent",
  135. // ListOptions is now part of the meta group
  136. "ListOptions",
  137. // DeleteOptions, CreateOptions and UpdateOptions are only read in metav1
  138. "DeleteOptions",
  139. "CreateOptions",
  140. "UpdateOptions",
  141. "PatchOptions",
  142. )
  143. var commonKinds = []string{"Status", "ListOptions", "DeleteOptions", "ExportOptions", "GetOptions", "CreateOptions", "UpdateOptions", "PatchOptions"}
  144. // TestCommonKindsRegistered verifies that all group/versions registered with
  145. // the legacyscheme package have the common kinds.
  146. func TestCommonKindsRegistered(t *testing.T) {
  147. gvs := map[schema.GroupVersion]bool{}
  148. for gvk := range legacyscheme.Scheme.AllKnownTypes() {
  149. if gvk.Version == runtime.APIVersionInternal {
  150. continue
  151. }
  152. gvs[gvk.GroupVersion()] = true
  153. }
  154. for _, kind := range commonKinds {
  155. for gv := range gvs {
  156. gvk := gv.WithKind(kind)
  157. obj, err := legacyscheme.Scheme.New(gvk)
  158. if err != nil {
  159. t.Error(err)
  160. }
  161. defaults := gv.WithKind("")
  162. var got *schema.GroupVersionKind
  163. if obj, got, err = legacyscheme.Codecs.LegacyCodec().Decode([]byte(`{"kind":"`+kind+`"}`), &defaults, obj); err != nil || gvk != *got {
  164. t.Errorf("expected %v: %v %v", gvk, got, err)
  165. }
  166. data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(gv), obj)
  167. if err != nil {
  168. t.Errorf("expected %v: %v\n%s", gvk, err, string(data))
  169. continue
  170. }
  171. if !bytes.Contains(data, []byte(`"kind":"`+kind+`","apiVersion":"`+gv.String()+`"`)) {
  172. if kind != "Status" {
  173. t.Errorf("expected %v: %v\n%s", gvk, err, string(data))
  174. continue
  175. }
  176. // TODO: this is wrong, but legacy clients expect it
  177. if !bytes.Contains(data, []byte(`"kind":"`+kind+`","apiVersion":"v1"`)) {
  178. t.Errorf("expected %v: %v\n%s", gvk, err, string(data))
  179. continue
  180. }
  181. }
  182. }
  183. }
  184. }
  185. // TestRoundTripTypes applies the round-trip test to all round-trippable Kinds
  186. // in all of the API groups registered for test in the legacyscheme package.
  187. func TestRoundTripTypes(t *testing.T) {
  188. seed := rand.Int63()
  189. fuzzer := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(seed), legacyscheme.Codecs)
  190. nonRoundTrippableTypes := map[schema.GroupVersionKind]bool{}
  191. roundtrip.RoundTripTypes(t, legacyscheme.Scheme, legacyscheme.Codecs, fuzzer, nonRoundTrippableTypes)
  192. }
  193. // TestEncodePtr tests that a pointer to a golang type can be encoded and
  194. // decoded without information loss or mutation.
  195. func TestEncodePtr(t *testing.T) {
  196. grace := int64(30)
  197. enableServiceLinks := v1.DefaultEnableServiceLinks
  198. preemptNever := api.PreemptNever
  199. pod := &api.Pod{
  200. ObjectMeta: metav1.ObjectMeta{
  201. Labels: map[string]string{"name": "foo"},
  202. },
  203. Spec: api.PodSpec{
  204. RestartPolicy: api.RestartPolicyAlways,
  205. DNSPolicy: api.DNSClusterFirst,
  206. TerminationGracePeriodSeconds: &grace,
  207. SecurityContext: &api.PodSecurityContext{},
  208. SchedulerName: api.DefaultSchedulerName,
  209. EnableServiceLinks: &enableServiceLinks,
  210. PreemptionPolicy: &preemptNever,
  211. },
  212. }
  213. obj := runtime.Object(pod)
  214. data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), obj)
  215. obj2, err2 := runtime.Decode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), data)
  216. if err != nil || err2 != nil {
  217. t.Fatalf("Failure: '%v' '%v'", err, err2)
  218. }
  219. if _, ok := obj2.(*api.Pod); !ok {
  220. t.Fatalf("Got wrong type")
  221. }
  222. if !apiequality.Semantic.DeepEqual(obj2, pod) {
  223. t.Errorf("\nExpected:\n\n %#v,\n\nGot:\n\n %#vDiff: %v\n\n", pod, obj2, diff.ObjectDiff(obj2, pod))
  224. }
  225. }
  226. func TestDecodeTimeStampWithoutQuotes(t *testing.T) {
  227. testYAML := []byte(`
  228. apiVersion: v1
  229. kind: Pod
  230. metadata:
  231. creationTimestamp: 2018-08-30T14:10:58Z
  232. name: test
  233. spec:
  234. containers: null
  235. status: {}`)
  236. if obj, err := runtime.Decode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), testYAML); err != nil {
  237. t.Fatalf("unable to decode yaml: %v", err)
  238. } else {
  239. if obj2, ok := obj.(*api.Pod); !ok {
  240. t.Fatalf("Got wrong type")
  241. } else {
  242. if obj2.ObjectMeta.CreationTimestamp.UnixNano() != parseTimeOrDie("2018-08-30T14:10:58Z").UnixNano() {
  243. t.Fatalf("Time stamps do not match")
  244. }
  245. }
  246. }
  247. }
  248. // TestBadJSONRejection establishes that a JSON object without a kind or with
  249. // an unknown kind will not be decoded without error.
  250. func TestBadJSONRejection(t *testing.T) {
  251. badJSONMissingKind := []byte(`{ }`)
  252. if _, err := runtime.Decode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), badJSONMissingKind); err == nil {
  253. t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
  254. }
  255. badJSONUnknownType := []byte(`{"kind": "bar"}`)
  256. if _, err1 := runtime.Decode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), badJSONUnknownType); err1 == nil {
  257. t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
  258. }
  259. }
  260. // TestUnversionedTypes establishes that the default codec can encode and
  261. // decode unversioned objects.
  262. func TestUnversionedTypes(t *testing.T) {
  263. testcases := []runtime.Object{
  264. &metav1.Status{Status: "Failure", Message: "something went wrong"},
  265. &metav1.APIVersions{Versions: []string{"A", "B", "C"}},
  266. &metav1.APIGroupList{Groups: []metav1.APIGroup{{Name: "mygroup"}}},
  267. &metav1.APIGroup{Name: "mygroup"},
  268. &metav1.APIResourceList{GroupVersion: "mygroup/myversion"},
  269. }
  270. for _, obj := range testcases {
  271. // Make sure the unversioned codec can encode
  272. unversionedJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), obj)
  273. if err != nil {
  274. t.Errorf("%v: unexpected error: %v", obj, err)
  275. continue
  276. }
  277. // Make sure the versioned codec under test can decode
  278. versionDecodedObject, err := runtime.Decode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), unversionedJSON)
  279. if err != nil {
  280. t.Errorf("%v: unexpected error: %v", obj, err)
  281. continue
  282. }
  283. // Make sure it decodes correctly
  284. if !reflect.DeepEqual(obj, versionDecodedObject) {
  285. t.Errorf("%v: expected %#v, got %#v", obj, obj, versionDecodedObject)
  286. continue
  287. }
  288. }
  289. }
  290. // TestObjectWatchFraming establishes that a watch event can be encoded and
  291. // decoded correctly through each of the supported RFC2046 media types.
  292. func TestObjectWatchFraming(t *testing.T) {
  293. f := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(benchmarkSeed), legacyscheme.Codecs)
  294. secret := &api.Secret{}
  295. f.Fuzz(secret)
  296. if secret.Data == nil {
  297. secret.Data = map[string][]byte{}
  298. }
  299. secret.Data["binary"] = []byte{0x00, 0x10, 0x30, 0x55, 0xff, 0x00}
  300. secret.Data["utf8"] = []byte("a string with \u0345 characters")
  301. secret.Data["long"] = bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x00}, 1000)
  302. converted, _ := legacyscheme.Scheme.ConvertToVersion(secret, v1.SchemeGroupVersion)
  303. v1secret := converted.(*v1.Secret)
  304. for _, info := range legacyscheme.Codecs.SupportedMediaTypes() {
  305. if info.StreamSerializer == nil {
  306. continue
  307. }
  308. s := info.StreamSerializer
  309. framer := s.Framer
  310. embedded := info.Serializer
  311. if embedded == nil {
  312. t.Errorf("no embedded serializer for %s", info.MediaType)
  313. continue
  314. }
  315. innerDecode := legacyscheme.Codecs.DecoderToVersion(embedded, api.SchemeGroupVersion)
  316. // write a single object through the framer and back out
  317. obj := &bytes.Buffer{}
  318. if err := s.Encode(v1secret, obj); err != nil {
  319. t.Fatal(err)
  320. }
  321. out := &bytes.Buffer{}
  322. w := framer.NewFrameWriter(out)
  323. if n, err := w.Write(obj.Bytes()); err != nil || n != len(obj.Bytes()) {
  324. t.Fatal(err)
  325. }
  326. sr := streaming.NewDecoder(framer.NewFrameReader(ioutil.NopCloser(out)), s)
  327. resultSecret := &v1.Secret{}
  328. res, _, err := sr.Decode(nil, resultSecret)
  329. if err != nil {
  330. t.Fatalf("%v:\n%s", err, hex.Dump(obj.Bytes()))
  331. }
  332. resultSecret.Kind = "Secret"
  333. resultSecret.APIVersion = "v1"
  334. if !apiequality.Semantic.DeepEqual(v1secret, res) {
  335. t.Fatalf("objects did not match: %s", diff.ObjectGoPrintDiff(v1secret, res))
  336. }
  337. // write a watch event through the frame writer and read it back in
  338. // via the frame reader for this media type
  339. obj = &bytes.Buffer{}
  340. if err := embedded.Encode(v1secret, obj); err != nil {
  341. t.Fatal(err)
  342. }
  343. event := &metav1.WatchEvent{Type: string(watch.Added)}
  344. event.Object.Raw = obj.Bytes()
  345. obj = &bytes.Buffer{}
  346. if err := s.Encode(event, obj); err != nil {
  347. t.Fatal(err)
  348. }
  349. out = &bytes.Buffer{}
  350. w = framer.NewFrameWriter(out)
  351. if n, err := w.Write(obj.Bytes()); err != nil || n != len(obj.Bytes()) {
  352. t.Fatal(err)
  353. }
  354. sr = streaming.NewDecoder(framer.NewFrameReader(ioutil.NopCloser(out)), s)
  355. outEvent := &metav1.WatchEvent{}
  356. _, _, err = sr.Decode(nil, outEvent)
  357. if err != nil || outEvent.Type != string(watch.Added) {
  358. t.Fatalf("%v: %#v", err, outEvent)
  359. }
  360. if outEvent.Object.Object == nil && outEvent.Object.Raw != nil {
  361. outEvent.Object.Object, err = runtime.Decode(innerDecode, outEvent.Object.Raw)
  362. if err != nil {
  363. t.Fatalf("%v:\n%s", err, hex.Dump(outEvent.Object.Raw))
  364. }
  365. }
  366. if !apiequality.Semantic.DeepEqual(secret, outEvent.Object.Object) {
  367. t.Fatalf("%s: did not match after frame decoding: %s", info.MediaType, diff.ObjectGoPrintDiff(secret, outEvent.Object.Object))
  368. }
  369. }
  370. }
  371. const benchmarkSeed = 100
  372. func benchmarkItems(b *testing.B) []v1.Pod {
  373. apiObjectFuzzer := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(benchmarkSeed), legacyscheme.Codecs)
  374. items := make([]v1.Pod, 10)
  375. for i := range items {
  376. var pod api.Pod
  377. apiObjectFuzzer.Fuzz(&pod)
  378. pod.Spec.InitContainers, pod.Status.InitContainerStatuses = nil, nil
  379. out, err := legacyscheme.Scheme.ConvertToVersion(&pod, v1.SchemeGroupVersion)
  380. if err != nil {
  381. panic(err)
  382. }
  383. items[i] = *out.(*v1.Pod)
  384. }
  385. return items
  386. }
  387. func benchmarkItemsList(b *testing.B, numItems int) v1.PodList {
  388. apiObjectFuzzer := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(benchmarkSeed), legacyscheme.Codecs)
  389. items := make([]v1.Pod, numItems)
  390. for i := range items {
  391. var pod api.Pod
  392. apiObjectFuzzer.Fuzz(&pod)
  393. pod.Spec.InitContainers, pod.Status.InitContainerStatuses = nil, nil
  394. out, err := legacyscheme.Scheme.ConvertToVersion(&pod, v1.SchemeGroupVersion)
  395. if err != nil {
  396. panic(err)
  397. }
  398. items[i] = *out.(*v1.Pod)
  399. }
  400. return v1.PodList{
  401. Items: items,
  402. }
  403. }
  404. // BenchmarkEncodeCodec measures the cost of performing a codec encode, which includes
  405. // reflection (to clear APIVersion and Kind)
  406. func BenchmarkEncodeCodec(b *testing.B) {
  407. items := benchmarkItems(b)
  408. width := len(items)
  409. b.ResetTimer()
  410. for i := 0; i < b.N; i++ {
  411. if _, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &items[i%width]); err != nil {
  412. b.Fatal(err)
  413. }
  414. }
  415. b.StopTimer()
  416. }
  417. // BenchmarkEncodeCodecFromInternal measures the cost of performing a codec encode,
  418. // including conversions.
  419. func BenchmarkEncodeCodecFromInternal(b *testing.B) {
  420. items := benchmarkItems(b)
  421. width := len(items)
  422. encodable := make([]api.Pod, width)
  423. for i := range items {
  424. if err := legacyscheme.Scheme.Convert(&items[i], &encodable[i], nil); err != nil {
  425. b.Fatal(err)
  426. }
  427. }
  428. b.ResetTimer()
  429. for i := 0; i < b.N; i++ {
  430. if _, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &encodable[i%width]); err != nil {
  431. b.Fatal(err)
  432. }
  433. }
  434. b.StopTimer()
  435. }
  436. // BenchmarkEncodeJSONMarshal provides a baseline for regular JSON encode performance
  437. func BenchmarkEncodeJSONMarshal(b *testing.B) {
  438. items := benchmarkItems(b)
  439. width := len(items)
  440. b.ResetTimer()
  441. for i := 0; i < b.N; i++ {
  442. if _, err := gojson.Marshal(&items[i%width]); err != nil {
  443. b.Fatal(err)
  444. }
  445. }
  446. b.StopTimer()
  447. }
  448. func BenchmarkDecodeCodec(b *testing.B) {
  449. codec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
  450. items := benchmarkItems(b)
  451. width := len(items)
  452. encoded := make([][]byte, width)
  453. for i := range items {
  454. data, err := runtime.Encode(codec, &items[i])
  455. if err != nil {
  456. b.Fatal(err)
  457. }
  458. encoded[i] = data
  459. }
  460. b.ResetTimer()
  461. for i := 0; i < b.N; i++ {
  462. if _, err := runtime.Decode(codec, encoded[i%width]); err != nil {
  463. b.Fatal(err)
  464. }
  465. }
  466. b.StopTimer()
  467. }
  468. func BenchmarkDecodeIntoExternalCodec(b *testing.B) {
  469. codec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
  470. items := benchmarkItems(b)
  471. width := len(items)
  472. encoded := make([][]byte, width)
  473. for i := range items {
  474. data, err := runtime.Encode(codec, &items[i])
  475. if err != nil {
  476. b.Fatal(err)
  477. }
  478. encoded[i] = data
  479. }
  480. b.ResetTimer()
  481. for i := 0; i < b.N; i++ {
  482. obj := v1.Pod{}
  483. if err := runtime.DecodeInto(codec, encoded[i%width], &obj); err != nil {
  484. b.Fatal(err)
  485. }
  486. }
  487. b.StopTimer()
  488. }
  489. func BenchmarkDecodeIntoInternalCodec(b *testing.B) {
  490. codec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
  491. items := benchmarkItems(b)
  492. width := len(items)
  493. encoded := make([][]byte, width)
  494. for i := range items {
  495. data, err := runtime.Encode(codec, &items[i])
  496. if err != nil {
  497. b.Fatal(err)
  498. }
  499. encoded[i] = data
  500. }
  501. b.ResetTimer()
  502. for i := 0; i < b.N; i++ {
  503. obj := api.Pod{}
  504. if err := runtime.DecodeInto(codec, encoded[i%width], &obj); err != nil {
  505. b.Fatal(err)
  506. }
  507. }
  508. b.StopTimer()
  509. }
  510. // BenchmarkDecodeJSON provides a baseline for regular JSON decode performance
  511. func BenchmarkDecodeIntoJSON(b *testing.B) {
  512. codec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
  513. items := benchmarkItems(b)
  514. width := len(items)
  515. encoded := make([][]byte, width)
  516. for i := range items {
  517. data, err := runtime.Encode(codec, &items[i])
  518. if err != nil {
  519. b.Fatal(err)
  520. }
  521. encoded[i] = data
  522. }
  523. b.ResetTimer()
  524. for i := 0; i < b.N; i++ {
  525. obj := v1.Pod{}
  526. if err := gojson.Unmarshal(encoded[i%width], &obj); err != nil {
  527. b.Fatal(err)
  528. }
  529. }
  530. b.StopTimer()
  531. }
  532. // BenchmarkDecodeIntoJSONCodecGenConfigFast provides a baseline
  533. // for JSON decode performance with jsoniter.ConfigFast
  534. func BenchmarkDecodeIntoJSONCodecGenConfigFast(b *testing.B) {
  535. kcodec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
  536. items := benchmarkItems(b)
  537. width := len(items)
  538. encoded := make([][]byte, width)
  539. for i := range items {
  540. data, err := runtime.Encode(kcodec, &items[i])
  541. if err != nil {
  542. b.Fatal(err)
  543. }
  544. encoded[i] = data
  545. }
  546. b.ResetTimer()
  547. for i := 0; i < b.N; i++ {
  548. obj := v1.Pod{}
  549. if err := jsoniter.ConfigFastest.Unmarshal(encoded[i%width], &obj); err != nil {
  550. b.Fatal(err)
  551. }
  552. }
  553. b.StopTimer()
  554. }
  555. // BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary provides a
  556. // baseline for JSON decode performance with
  557. // jsoniter.ConfigCompatibleWithStandardLibrary, but with case sensitivity set
  558. // to true
  559. func BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary(b *testing.B) {
  560. kcodec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
  561. items := benchmarkItems(b)
  562. width := len(items)
  563. encoded := make([][]byte, width)
  564. for i := range items {
  565. data, err := runtime.Encode(kcodec, &items[i])
  566. if err != nil {
  567. b.Fatal(err)
  568. }
  569. encoded[i] = data
  570. }
  571. b.ResetTimer()
  572. iter := json.CaseSensitiveJsonIterator()
  573. for i := 0; i < b.N; i++ {
  574. obj := v1.Pod{}
  575. if err := iter.Unmarshal(encoded[i%width], &obj); err != nil {
  576. b.Fatal(err)
  577. }
  578. }
  579. b.StopTimer()
  580. }
  581. // BenchmarkEncodeYAMLMarshal provides a baseline for regular YAML encode performance
  582. func BenchmarkEncodeYAMLMarshal(b *testing.B) {
  583. items := benchmarkItems(b)
  584. width := len(items)
  585. b.ResetTimer()
  586. for i := 0; i < b.N; i++ {
  587. if _, err := yaml.Marshal(&items[i%width]); err != nil {
  588. b.Fatal(err)
  589. }
  590. }
  591. b.StopTimer()
  592. }
  593. // BenchmarkDecodeYAML provides a baseline for regular YAML decode performance
  594. func BenchmarkDecodeIntoYAML(b *testing.B) {
  595. codec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
  596. items := benchmarkItems(b)
  597. width := len(items)
  598. encoded := make([][]byte, width)
  599. for i := range items {
  600. data, err := runtime.Encode(codec, &items[i])
  601. if err != nil {
  602. b.Fatal(err)
  603. }
  604. encoded[i] = data
  605. }
  606. b.ResetTimer()
  607. for i := 0; i < b.N; i++ {
  608. obj := v1.Pod{}
  609. if err := yaml.Unmarshal(encoded[i%width], &obj); err != nil {
  610. b.Fatal(err)
  611. }
  612. }
  613. b.StopTimer()
  614. }