From de0456bb5ab22e568b8a3ffcce6bf85e2a7de164 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 20 Apr 2021 14:11:30 +0200 Subject: pkg/config: use DisallowUnknownFields DisallowUnknownFields was added in Go 1.10, remove our custom machinery for checking unknown fields. --- pkg/config/config.go | 88 +------------------- pkg/config/config_test.go | 206 ---------------------------------------------- 2 files changed, 4 insertions(+), 290 deletions(-) delete mode 100644 pkg/config/config_test.go (limited to 'pkg') diff --git a/pkg/config/config.go b/pkg/config/config.go index 49d030499..a4f56c225 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -4,11 +4,10 @@ package config import ( + "bytes" "encoding/json" "fmt" "io/ioutil" - "reflect" - "strings" "github.com/google/syzkaller/pkg/osutil" ) @@ -25,10 +24,9 @@ func LoadFile(filename string, cfg interface{}) error { } func LoadData(data []byte, cfg interface{}) error { - if err := checkUnknownFields(data, reflect.ValueOf(cfg).Type()); err != nil { - return err - } - if err := json.Unmarshal(data, cfg); err != nil { + dec := json.NewDecoder(bytes.NewReader(data)) + dec.DisallowUnknownFields() + if err := dec.Decode(cfg); err != nil { return fmt.Errorf("failed to parse config file: %v", err) } return nil @@ -45,81 +43,3 @@ func SaveFile(filename string, cfg interface{}) error { func SaveData(cfg interface{}) ([]byte, error) { return json.MarshalIndent(cfg, "", "\t") } - -func checkUnknownFields(data []byte, typ reflect.Type) error { - if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Struct { - return fmt.Errorf("config type is not pointer to struct") - } - return checkUnknownFieldsRec(data, "", typ) -} - -func checkUnknownFieldsRec(data []byte, prefix string, typ reflect.Type) error { - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - } - if typ.Kind() != reflect.Struct { - return fmt.Errorf("config type is not pointer to struct") - } - fields := make(map[string]reflect.Type) - for i := 0; i < typ.NumField(); i++ { - field := typ.Field(i) - tag := field.Tag.Get("json") - tag = strings.TrimSuffix(tag, ",omitempty") - if tag == "-" { - continue - } - name := strings.ToLower(field.Name) - if tag != "" { - if tag != strings.ToLower(tag) { - return fmt.Errorf("json tag on '%v%v' should be lower-case", prefix, name) - } - name = tag - } - fields[name] = field.Type - } - f := make(map[string]interface{}) - if err := json.Unmarshal(data, &f); err != nil { - return fmt.Errorf("failed to parse config file: %v", err) - } - for k, v := range f { - field, ok := fields[strings.ToLower(k)] - if !ok { - return fmt.Errorf("unknown field '%v%v' in config", prefix, k) - } - if v != nil && field.Kind() == reflect.Slice && - (field.PkgPath() != "encoding/json" || field.Name() != "RawMessage") { - vv := reflect.ValueOf(v) - if vv.Type().Kind() != reflect.Slice { - return fmt.Errorf("bad json array type '%v%v'", prefix, k) - } - for i := 0; i < vv.Len(); i++ { - e := vv.Index(i).Interface() - prefix1 := fmt.Sprintf("%v%v[%v].", prefix, k, i) - if err := checkUnknownFieldsStruct(e, prefix1, field.Elem()); err != nil { - return err - } - } - } - if err := checkUnknownFieldsStruct(v, prefix+k+".", field); err != nil { - return err - } - } - return nil -} - -func checkUnknownFieldsStruct(val interface{}, prefix string, typ reflect.Type) error { - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - } - if typ.Kind() != reflect.Struct { - return nil - } - if typ.PkgPath() == "time" && typ.Name() == "Time" { - return nil - } - inner, err := json.Marshal(val) - if err != nil { - return fmt.Errorf("failed to marshal inner struct %q: %v", prefix, err) - } - return checkUnknownFieldsRec(inner, prefix, typ) -} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go deleted file mode 100644 index c8cd3e6ed..000000000 --- a/pkg/config/config_test.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2016 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -package config - -import ( - "encoding/json" - "fmt" - "reflect" - "testing" - "time" -) - -func TestLoad(t *testing.T) { - type NestedNested struct { - Ccc int - Ddd string - } - type Nested struct { - Aaa int - Bbb string - More NestedNested - } - type Config struct { - Foo int - Bar string - Baz string `json:"-"` - Raw json.RawMessage - Qux []string - Box Nested - Boq *Nested - Arr []Nested - T time.Time - } - - tests := []struct { - input string - output Config - err string - }{ - { - `{"foo": 42}`, - Config{ - Foo: 42, - }, - "", - }, - { - `{"BAR": "Baz", "foo": 42}`, - Config{ - Foo: 42, - Bar: "Baz", - }, - "", - }, - { - `{"foobar": 42}`, - Config{}, - "unknown field 'foobar' in config", - }, - { - `{"foo": 1, "baz": "baz", "bar": "bar"}`, - Config{}, - "unknown field 'baz' in config", - }, - { - `{"foo": 1, "box": {"aaa": 12, "bbb": "bbb"}}`, - Config{ - Foo: 1, - Box: Nested{ - Aaa: 12, - Bbb: "bbb", - }, - }, - "", - }, - { - `{"qux": ["aaa", "bbb"]}`, - Config{ - Qux: []string{"aaa", "bbb"}, - }, - "", - }, - { - `{"box": {"aaa": 12, "ccc": "bbb"}}`, - Config{}, - "unknown field 'box.ccc' in config", - }, - { - `{"foo": 1, "boq": {"aaa": 12, "bbb": "bbb"}}`, - Config{ - Foo: 1, - Boq: &Nested{ - Aaa: 12, - Bbb: "bbb", - }, - }, - "", - }, - { - `{"boq": {"aaa": 12, "ccc": "bbb"}}`, - Config{}, - "unknown field 'boq.ccc' in config", - }, - - { - `{"foo": 1, "arr": []}`, - Config{ - Foo: 1, - Arr: []Nested{}, - }, - "", - }, - { - `{"foo": 1, "arr": [{"aaa": 12, "bbb": "bbb"}, {"aaa": 13, "bbb": "ccc"}]}`, - Config{ - Foo: 1, - Arr: []Nested{ - { - Aaa: 12, - Bbb: "bbb", - }, - { - Aaa: 13, - Bbb: "ccc", - }, - }, - }, - "", - }, - { - `{"arr": [{"aaa": 12, "ccc": "bbb"}]}`, - Config{}, - "unknown field 'arr[0].ccc' in config", - }, - { - `{"foo": 1, "boq": {"aaa": 12, "more": {"ccc": 13, "ddd": "ddd"}}}`, - Config{ - Foo: 1, - Boq: &Nested{ - Aaa: 12, - More: NestedNested{ - Ccc: 13, - Ddd: "ddd", - }, - }, - }, - "", - }, - { - `{"foo": 1, "boq": {"aaa": 12, "more": {"ccc": 13, "eee": "eee"}}}`, - Config{}, - "unknown field 'boq.more.eee' in config", - }, - { - `{"raw": {"zux": 11}}`, - Config{ - Raw: []byte(`{"zux": 11}`), - }, - "", - }, - { - `{"foo": null, "qux": null}`, - Config{}, - "", - }, - { - `{"t": "2000-01-02T03:04:05Z"}`, - Config{ - T: time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC), - }, - "", - }, - } - for i, test := range tests { - t.Run(fmt.Sprint(i), func(t *testing.T) { - var cfg Config - err := LoadData([]byte(test.input), &cfg) - errStr := "" - if err != nil { - errStr = err.Error() - } - if test.err != errStr { - t.Fatalf("bad err: want '%v', got '%v'", test.err, errStr) - } - if !reflect.DeepEqual(test.output, cfg) { - t.Fatalf("bad output: want:\n%#v\n, got:\n%#v", test.output, cfg) - } - }) - } -} - -func TestLoadBadType(t *testing.T) { - want := "config type is not pointer to struct" - if err := LoadData([]byte("{}"), 1); err == nil || err.Error() != want { - t.Fatalf("got '%v', want '%v'", err, want) - } - i := 0 - if err := LoadData([]byte("{}"), &i); err == nil || err.Error() != want { - t.Fatalf("got '%v', want '%v'", err, want) - } - s := struct{}{} - if err := LoadData([]byte("{}"), s); err == nil || err.Error() != want { - t.Fatalf("got '%v', want '%v'", err, want) - } -} -- cgit mrf-deployment