From 780b27623e2c0284dfb0c8e0392463d1eef00e92 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 17 Nov 2021 12:36:48 +0000 Subject: tools/syz-testbed: support per-checkout syz-manager configs Let the user of the tool specify individual syz-manager configs for checkouts. A base config is specified as previously and then individual parts of jsons are applied to that base config, e.g. <...> "checkouts": [ { "name": "first", "repo": "https://github.com/google/syzkaller.git", }, { "name": "second", "repo": "https://github.com/google/syzkaller.git", "manager_config": { "kernel_obj": "/tmp/linux-stable2" } } ], "manager_config": { "target": "linux/amd64", "kernel_obj": "/tmp/linux-stable", <...> } <...> --- pkg/config/merge.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ pkg/config/merge_test.go | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 pkg/config/merge.go create mode 100644 pkg/config/merge_test.go (limited to 'pkg') diff --git a/pkg/config/merge.go b/pkg/config/merge.go new file mode 100644 index 000000000..49a7254de --- /dev/null +++ b/pkg/config/merge.go @@ -0,0 +1,57 @@ +// Copyright 2021 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" +) + +// Unfortunately, if we want to apply a JSON patch to some configuration, we cannot just unmarshal +// it twice - in that case json.RawMessage objects will be completely replaced, but not merged. +func MergeJSONData(left, right []byte) ([]byte, error) { + vLeft, err := parseFragment(left) + if err != nil { + return nil, err + } + vRight, err := parseFragment(right) + if err != nil { + return nil, err + } + merged := mergeRecursive(vLeft, vRight) + return json.Marshal(merged) +} + +func parseFragment(input []byte) (parsed interface{}, err error) { + if len(input) == 0 { + // For convenience, we allow empty strings to be passed to the function that merges JSONs. + return + } + err = json.Unmarshal(json.RawMessage(input), &parsed) + return +} + +// If one of the elements is not a map, use the new one. +// Otherwise, recursively merge map elements. +func mergeRecursive(left, right interface{}) interface{} { + if left == nil { + return right + } + if right == nil { + return left + } + mLeft, okLeft := left.(map[string]interface{}) + mRight, okRight := right.(map[string]interface{}) + if !okLeft || !okRight { + return right + } + for key, val := range mRight { + valLeft, ok := mLeft[key] + if ok { + mLeft[key] = mergeRecursive(valLeft, val) + } else { + mLeft[key] = val + } + } + return mLeft +} diff --git a/pkg/config/merge_test.go b/pkg/config/merge_test.go new file mode 100644 index 000000000..c5a45754e --- /dev/null +++ b/pkg/config/merge_test.go @@ -0,0 +1,49 @@ +// Copyright 2021 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_test + +import ( + "bytes" + "testing" + + "github.com/google/syzkaller/pkg/config" +) + +func TestMergeJSONData(t *testing.T) { + tests := []struct { + left string + right string + result string + }{ + { + `{"a":1,"b":2}`, + `{"b":3,"c":4}`, + `{"a":1,"b":3,"c":4}`, + }, + { + `{"a":1,"b":{"c":{"d":"nested string","e":"another string"}}}`, + `{"b":{"c":{"d":12345}}}`, + `{"a":1,"b":{"c":{"d":12345,"e":"another string"}}}`, + }, + { + `{}`, + `{"a":{"b":{"c":0}}}`, + `{"a":{"b":{"c":0}}}`, + }, + { + `{"a":{"b":{"c":0}}}`, + ``, + `{"a":{"b":{"c":0}}}`, + }, + } + for _, test := range tests { + res, err := config.MergeJSONData([]byte(test.left), []byte(test.right)) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + if !bytes.Equal(res, []byte(test.result)) { + t.Errorf("expected %s, got %s", test.result, res) + } + } +} -- cgit mrf-deployment