aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/config
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2021-04-20 14:11:30 +0200
committerDmitry Vyukov <dvyukov@google.com>2021-04-28 07:08:41 +0200
commitde0456bb5ab22e568b8a3ffcce6bf85e2a7de164 (patch)
tree1d8603e4ee455b2c6c4e5ea36d42e28ce7977b1c /pkg/config
parent805b5003de8c474f5d317a67f739597ebc78fcca (diff)
pkg/config: use DisallowUnknownFields
DisallowUnknownFields was added in Go 1.10, remove our custom machinery for checking unknown fields.
Diffstat (limited to 'pkg/config')
-rw-r--r--pkg/config/config.go88
-rw-r--r--pkg/config/config_test.go206
2 files changed, 4 insertions, 290 deletions
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)
- }
-}