aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/config
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-06-01 11:17:54 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-06-01 11:17:54 +0200
commit84eb5fd389bc4ae247048d1f20ec9577935f4f04 (patch)
treeb7ff3a69478dc0965788e84e5c5ddbb35a9f3e01 /pkg/config
parent2cf166ec847e808c72cc8942d696d7c66e78acff (diff)
config: split and refactor
Introduce generic config.Load function that can be reused across multiple programs (syz-manager, syz-gce, etc). Move the generic config functionality to pkg/config package. The idea is to move all helper (non-main) packages to pkg/ dir, because we have more and more of them and they pollute the top dir. Move the syz-manager config parts into syz-manager/config package.
Diffstat (limited to 'pkg/config')
-rw-r--r--pkg/config/config.go61
-rw-r--r--pkg/config/config_test.go65
2 files changed, 126 insertions, 0 deletions
diff --git a/pkg/config/config.go b/pkg/config/config.go
new file mode 100644
index 000000000..feb031c71
--- /dev/null
+++ b/pkg/config/config.go
@@ -0,0 +1,61 @@
+// Copyright 2017 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"
+ "io/ioutil"
+ "reflect"
+ "strings"
+)
+
+func Load(filename string, cfg interface{}) error {
+ if filename == "" {
+ return fmt.Errorf("no config file specified")
+ }
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return fmt.Errorf("failed to read config file: %v", err)
+ }
+ return load(data, cfg)
+}
+
+func load(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 {
+ return fmt.Errorf("failed to parse config file: %v", err)
+ }
+ return nil
+}
+
+func checkUnknownFields(data []byte, typ reflect.Type) error {
+ if typ.Kind() != reflect.Ptr {
+ return fmt.Errorf("config type is not pointer to struct")
+ }
+ typ = typ.Elem()
+ if typ.Kind() != reflect.Struct {
+ return fmt.Errorf("config type is not pointer to struct")
+ }
+ fields := make(map[string]bool)
+ for i := 0; i < typ.NumField(); i++ {
+ field := typ.Field(i)
+ if field.Tag.Get("json") == "-" {
+ continue
+ }
+ fields[strings.ToLower(field.Name)] = true
+ }
+ 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 := range f {
+ if !fields[strings.ToLower(k)] {
+ return fmt.Errorf("unknown field '%v' in config", k)
+ }
+ }
+ return nil
+}
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
new file mode 100644
index 000000000..73ad3502c
--- /dev/null
+++ b/pkg/config/config_test.go
@@ -0,0 +1,65 @@
+// 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 (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+func TestUnknown(t *testing.T) {
+ type Config struct {
+ Foo int
+ Bar string
+ Baz string `json:"-"`
+ }
+ 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",
+ },
+ }
+ for i, test := range tests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ var cfg Config
+ err := load([]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 '%#v', got '%#v'", test.output, cfg)
+ }
+ })
+ }
+}