From dcdc911bd3f27d98bf5617b9fdd8849b1bad4b42 Mon Sep 17 00:00:00 2001 From: Jon Stevens Date: Tue, 20 Mar 2018 14:48:11 +0700 Subject: [PATCH] recursively merge json objects instead of just merging keys --- service/agent/config_test.go | 47 ++++++++++++++++++++++++++++++++++++ tool/json.go | 40 ++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/service/agent/config_test.go b/service/agent/config_test.go index 507e547..c17e0f2 100644 --- a/service/agent/config_test.go +++ b/service/agent/config_test.go @@ -23,6 +23,26 @@ const priorConfigVersion = `{ } ` +const failingConfig = ` +{ + "cmdLine": { + "port": ":1111" + }, + "controller": { + "reboot": { + "delay": "5s" + } + }, + "monitor": { + "load": { + "period_secs": 60, + "high_load_Mark": 5, + "enabled": true + } + } +} +` + func defaultConfig() []byte { data, _ := ioutil.ReadFile("../../conf/bam_agent.json") return data @@ -72,6 +92,33 @@ func TestNewConfig(t *testing.T) { } } +func TestLoadingOldConfig(t *testing.T) { + file, err := ioutil.TempFile("", "agent-config") + defer os.Remove(file.Name()) + + // Write out an old version of a file + ioutil.WriteFile(file.Name(), []byte(failingConfig), 644) + file.Close() + + if err != nil { + t.Fatal(err) + } + + cfg := NewConfig(tool.CmdLine{ + AgentConfigPath: file.Name(), + }, defaultConfig()) + + if !cfg.Loaded().Monitor.HighLoad.Enabled { + t.Fatalf("expected highLoad to be enabled") + } + + // We should have saved the file as part of the load + fileData, err := ioutil.ReadFile(file.Name()) + if !strings.Contains(string(fileData), "highLoad") { + t.Fatalf("expected file data to have updated port number") + } +} + // simulate a BAM agent binary update that adds a structure to the default BAM interface // by saving a previous config file that doesnt have the current monitors in it func TestStructChangeToConfig(t *testing.T) { diff --git a/tool/json.go b/tool/json.go index df799af..6b6b2fe 100644 --- a/tool/json.go +++ b/tool/json.go @@ -89,17 +89,47 @@ func getRandomizedDuration(duration time.Duration) time.Duration { } /* - Merges source into/over destination + Recursively merges source into/over destination */ func Merge(src []byte, dst []byte) ([]byte, error) { - var mergeMap map[string]interface{} - err := jsoniter.Unmarshal(dst, &mergeMap) + var err error + + var srcObj interface{} + err = jsoniter.Unmarshal(src, &srcObj) if err != nil { return nil, err } - err = jsoniter.Unmarshal(src, &mergeMap) + + var dstObj interface{} + err = jsoniter.Unmarshal(dst, &dstObj) if err != nil { return nil, err } - return jsoniter.Marshal(mergeMap) + + return jsoniter.Marshal(merge1(srcObj, dstObj)) +} + +// https://play.golang.org/p/8jlJUbEJKf +func merge1(x1, x2 interface{}) interface{} { + switch x1 := x1.(type) { + case map[string]interface{}: + x2, ok := x2.(map[string]interface{}) + if !ok { + return x1 + } + for k, v2 := range x2 { + if v1, ok := x1[k]; ok { + x1[k] = merge1(v1, v2) + } else { + x1[k] = v2 + } + } + case nil: + // merge(nil, map[string]interface{...}) -> map[string]interface{...} + x2, ok := x2.(map[string]interface{}) + if ok { + return x2 + } + } + return x1 }