Commit a78ef374 authored by Chao Xu's avatar Chao Xu

make json serializer case sensitive

parent 0bf82f28
...@@ -58,7 +58,6 @@ go_library( ...@@ -58,7 +58,6 @@ go_library(
"//pkg/kubelet/apis/kubeletconfig/v1beta1:go_default_library", "//pkg/kubelet/apis/kubeletconfig/v1beta1:go_default_library",
"//pkg/proxy/apis/kubeproxyconfig/scheme:go_default_library", "//pkg/proxy/apis/kubeproxyconfig/scheme:go_default_library",
"//pkg/proxy/apis/kubeproxyconfig/v1alpha1:go_default_library", "//pkg/proxy/apis/kubeproxyconfig/v1alpha1:go_default_library",
"//vendor/github.com/json-iterator/go:go_default_library",
"//vendor/github.com/ugorji/go/codec:go_default_library", "//vendor/github.com/ugorji/go/codec:go_default_library",
"//vendor/gopkg.in/yaml.v2:go_default_library", "//vendor/gopkg.in/yaml.v2:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
......
...@@ -24,7 +24,6 @@ import ( ...@@ -24,7 +24,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/json-iterator/go"
"github.com/ugorji/go/codec" "github.com/ugorji/go/codec"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
...@@ -33,8 +32,6 @@ import ( ...@@ -33,8 +32,6 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
) )
var json = jsoniter.ConfigCompatibleWithStandardLibrary
type configMutationFunc func(map[string]interface{}) error type configMutationFunc func(map[string]interface{}) error
// These migrations are a stop-gap until we get a properly-versioned configuration file for MasterConfiguration. // These migrations are a stop-gap until we get a properly-versioned configuration file for MasterConfiguration.
......
...@@ -96,6 +96,7 @@ go_test( ...@@ -96,6 +96,7 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/protobuf:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/protobuf:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
......
...@@ -37,6 +37,7 @@ import ( ...@@ -37,6 +37,7 @@ import (
"k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
k8s_json "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming" "k8s.io/apimachinery/pkg/runtime/serializer/streaming"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
...@@ -557,9 +558,10 @@ func BenchmarkDecodeIntoJSONCodecGenConfigFast(b *testing.B) { ...@@ -557,9 +558,10 @@ func BenchmarkDecodeIntoJSONCodecGenConfigFast(b *testing.B) {
b.StopTimer() b.StopTimer()
} }
// BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary // BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary provides a
// provides a baseline for JSON decode performance // baseline for JSON decode performance with
// with jsoniter.ConfigCompatibleWithStandardLibrary // jsoniter.ConfigCompatibleWithStandardLibrary, but with case sensitivity set
// to true
func BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary(b *testing.B) { func BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary(b *testing.B) {
kcodec := testapi.Default.Codec() kcodec := testapi.Default.Codec()
items := benchmarkItems(b) items := benchmarkItems(b)
...@@ -574,9 +576,10 @@ func BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary(b *testi ...@@ -574,9 +576,10 @@ func BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary(b *testi
} }
b.ResetTimer() b.ResetTimer()
iter := k8s_json.CaseSensitiveJsonIterator()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
obj := v1.Pod{} obj := v1.Pod{}
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(encoded[i%width], &obj); err != nil { if err := iter.Unmarshal(encoded[i%width], &obj); err != nil {
b.Fatal(err) b.Fatal(err)
} }
} }
......
...@@ -21,9 +21,9 @@ go_test( ...@@ -21,9 +21,9 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//vendor/github.com/ghodss/yaml:go_default_library", "//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/json-iterator/go:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
], ],
) )
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
"reflect" "reflect"
"testing" "testing"
jsoniter "github.com/json-iterator/go" k8s_json "k8s.io/apimachinery/pkg/runtime/serializer/json"
) )
type GroupVersionHolder struct { type GroupVersionHolder struct {
...@@ -47,7 +47,8 @@ func TestGroupVersionUnmarshalJSON(t *testing.T) { ...@@ -47,7 +47,8 @@ func TestGroupVersionUnmarshalJSON(t *testing.T) {
t.Errorf("JSON codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV) t.Errorf("JSON codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV)
} }
// test the json-iterator codec // test the json-iterator codec
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(c.input, &result); err != nil { iter := k8s_json.CaseSensitiveJsonIterator()
if err := iter.Unmarshal(c.input, &result); err != nil {
t.Errorf("json-iterator codec failed to unmarshal input '%v': %v", c.input, err) t.Errorf("json-iterator codec failed to unmarshal input '%v': %v", c.input, err)
} }
if !reflect.DeepEqual(result.GV, c.expect) { if !reflect.DeepEqual(result.GV, c.expect) {
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
"reflect" "reflect"
"testing" "testing"
jsoniter "github.com/json-iterator/go" k8s_json "k8s.io/apimachinery/pkg/runtime/serializer/json"
) )
func TestVerbsUgorjiMarshalJSON(t *testing.T) { func TestVerbsUgorjiMarshalJSON(t *testing.T) {
...@@ -45,7 +45,7 @@ func TestVerbsUgorjiMarshalJSON(t *testing.T) { ...@@ -45,7 +45,7 @@ func TestVerbsUgorjiMarshalJSON(t *testing.T) {
} }
} }
func TestVerbsUgorjiUnmarshalJSON(t *testing.T) { func TestVerbsJsonIterUnmarshalJSON(t *testing.T) {
cases := []struct { cases := []struct {
input string input string
result APIResource result APIResource
...@@ -56,9 +56,10 @@ func TestVerbsUgorjiUnmarshalJSON(t *testing.T) { ...@@ -56,9 +56,10 @@ func TestVerbsUgorjiUnmarshalJSON(t *testing.T) {
{`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}}, {`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}},
} }
iter := k8s_json.CaseSensitiveJsonIterator()
for i, c := range cases { for i, c := range cases {
var result APIResource var result APIResource
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(c.input), &result); err != nil { if err := iter.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err) t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err)
} }
if !reflect.DeepEqual(result, c.result) { if !reflect.DeepEqual(result, c.result) {
......
...@@ -98,6 +98,20 @@ func init() { ...@@ -98,6 +98,20 @@ func init() {
jsoniter.RegisterTypeDecoderFunc("interface {}", decodeNumberAsInt64IfPossible) jsoniter.RegisterTypeDecoderFunc("interface {}", decodeNumberAsInt64IfPossible)
} }
// CaseSensitiveJsonIterator returns a jsoniterator API that's configured to be
// case-sensitive when unmarshalling, and otherwise compatible with
// the encoding/json standard library.
func CaseSensitiveJsonIterator() jsoniter.API {
return jsoniter.Config{
EscapeHTML: true,
SortMapKeys: true,
ValidateJsonRawMessage: true,
CaseSensitive: true,
}.Froze()
}
var caseSensitiveJsonIterator = CaseSensitiveJsonIterator()
// gvkWithDefaults returns group kind and version defaulting from provided default // gvkWithDefaults returns group kind and version defaulting from provided default
func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind { func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind {
if len(actual.Kind) == 0 { if len(actual.Kind) == 0 {
...@@ -162,7 +176,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i ...@@ -162,7 +176,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
types, _, err := s.typer.ObjectKinds(into) types, _, err := s.typer.ObjectKinds(into)
switch { switch {
case runtime.IsNotRegisteredError(err), isUnstructured: case runtime.IsNotRegisteredError(err), isUnstructured:
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(data, into); err != nil { if err := caseSensitiveJsonIterator.Unmarshal(data, into); err != nil {
return nil, actual, err return nil, actual, err
} }
return into, actual, nil return into, actual, nil
...@@ -186,7 +200,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i ...@@ -186,7 +200,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
return nil, actual, err return nil, actual, err
} }
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(data, obj); err != nil { if err := caseSensitiveJsonIterator.Unmarshal(data, obj); err != nil {
return nil, actual, err return nil, actual, err
} }
return obj, actual, nil return obj, actual, nil
...@@ -195,7 +209,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i ...@@ -195,7 +209,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
// Encode serializes the provided object to the given writer. // Encode serializes the provided object to the given writer.
func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error { func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
if s.yaml { if s.yaml {
json, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(obj) json, err := caseSensitiveJsonIterator.Marshal(obj)
if err != nil { if err != nil {
return err return err
} }
...@@ -208,7 +222,7 @@ func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error { ...@@ -208,7 +222,7 @@ func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
} }
if s.pretty { if s.pretty {
data, err := jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ") data, err := caseSensitiveJsonIterator.MarshalIndent(obj, "", " ")
if err != nil { if err != nil {
return err return err
} }
......
...@@ -30,10 +30,31 @@ import ( ...@@ -30,10 +30,31 @@ import (
type testDecodable struct { type testDecodable struct {
Other string Other string
Value int `json:"value"` Value int `json:"value"`
Spec DecodableSpec `json:"spec"`
gvk schema.GroupVersionKind gvk schema.GroupVersionKind
} }
// DecodableSpec has 15 fields. json-iterator treats struct with more than 10
// fields differently from struct that has less than 10 fields.
type DecodableSpec struct {
A int `json:"A"`
B int `json:"B"`
C int `json:"C"`
D int `json:"D"`
E int `json:"E"`
F int `json:"F"`
G int `json:"G"`
H int `json:"h"`
I int `json:"i"`
J int `json:"j"`
K int `json:"k"`
L int `json:"l"`
M int `json:"m"`
N int `json:"n"`
O int `json:"o"`
}
func (d *testDecodable) GetObjectKind() schema.ObjectKind { return d } func (d *testDecodable) GetObjectKind() schema.ObjectKind { return d }
func (d *testDecodable) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk } func (d *testDecodable) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk }
func (d *testDecodable) GroupVersionKind() schema.GroupVersionKind { return d.gvk } func (d *testDecodable) GroupVersionKind() schema.GroupVersionKind { return d.gvk }
...@@ -221,6 +242,28 @@ func TestDecode(t *testing.T) { ...@@ -221,6 +242,28 @@ func TestDecode(t *testing.T) {
}, },
}, },
}, },
// Unmarshalling is case-sensitive
{
// "VaLue" should have been "value"
data: []byte(`{"kind":"Test","apiVersion":"other/blah","VaLue":1,"Other":"test"}`),
into: &testDecodable{},
typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind(schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &testDecodable{
Other: "test",
},
},
// Unmarshalling is case-sensitive for big struct.
{
// "b" should have been "B", "I" should have been "i"
data: []byte(`{"kind":"Test","apiVersion":"other/blah","spec": {"A": 1, "b": 2, "h": 3, "I": 4}}`),
into: &testDecodable{},
typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind(schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &testDecodable{
Spec: DecodableSpec{A: 1, H: 3},
},
},
} }
for i, test := range testCases { for i, test := range testCases {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment