From 0b50c4763a5d65d5d4160bc0c53d907c73debcea Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 25 Nov 2020 13:59:17 +0200 Subject: [PATCH] Added DryRun option for ReloadConfig --- apier/v1/config_it_test.go | 81 ++++++++++++++++++++++++++++++++++++++ config/config.go | 61 +++++++++++++++++----------- config/config_it_test.go | 23 +++++++++++ config/config_test.go | 19 +++++++++ 4 files changed, 162 insertions(+), 22 deletions(-) diff --git a/apier/v1/config_it_test.go b/apier/v1/config_it_test.go index af3fc375c..b88f1f3b9 100644 --- a/apier/v1/config_it_test.go +++ b/apier/v1/config_it_test.go @@ -48,9 +48,12 @@ var ( testConfigSStartEngine, testConfigSRPCConn, testConfigSSetConfigSessionS, + testConfigSSetConfigEEsDryRun, testConfigSSetConfigEEs, testConfigSv1GetJSONSectionWithoutTenant, + testConfigSSetConfigFromJSONCoreSDryRun, testConfigSSetConfigFromJSONCoreS, + testConfigSReloadConfigCoreSDryRun, testConfigSReloadConfigCoreS, testConfigSKillEngine, testConfigStartEngineWithConfigs, @@ -231,6 +234,34 @@ func testConfigSv1GetJSONSectionWithoutTenant(t *testing.T) { } } +func testConfigSSetConfigEEsDryRun(t *testing.T) { + if *encoding == utils.MetaGOB { + t.SkipNow() + } + var reply string + if err := configRPC.Call(utils.ConfigSv1SetConfig, &config.SetConfigArgs{ + Config: map[string]interface{}{ + "ees": map[string]interface{}{ + "enabled": true, + }, + }, + DryRun: true, + }, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected OK received: %s", reply) + } + + var rpl map[string]interface{} + if err := configRPC.Call(utils.ConfigSv1GetConfig, &config.SectionWithOpts{ + Section: config.EEsJson, + }, &rpl); err != nil { + t.Error(err) + } else if rpl[config.EEsJson].(map[string]interface{})["enabled"] != false { + t.Errorf("Expected EEs to not change , received: %+v ", utils.ToJSON(rpl)) + } +} + func testConfigSSetConfigEEs(t *testing.T) { if *encoding == utils.MetaGOB { t.SkipNow() @@ -348,6 +379,31 @@ func testConfigStartEngineFromHTTP(t *testing.T) { } } +func testConfigSSetConfigFromJSONCoreSDryRun(t *testing.T) { + cfgStr := `{"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*queue","shutdown_timeout":"1s"}}` + var reply string + if err := configRPC.Call(utils.ConfigSv1SetConfigFromJSON, &config.SetConfigFromJSONArgs{ + Tenant: "cgrates.org", + Config: cfgStr, + DryRun: true, + }, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected OK received: %s", reply) + } + + expCfg := "{\"cores\":{\"caps\":0,\"caps_stats_interval\":\"0\",\"caps_strategy\":\"*busy\",\"shutdown_timeout\":\"1s\"}}" + var rpl string + if err := configRPC.Call(utils.ConfigSv1GetConfigAsJSON, &config.SectionWithOpts{ + Tenant: "cgrates.org", + Section: config.CoreSCfgJson, + }, &rpl); err != nil { + t.Error(err) + } else if expCfg != rpl { + t.Errorf("Expected %q , received: %q", expCfg, rpl) + } +} + func testConfigSSetConfigFromJSONCoreS(t *testing.T) { cfgStr := `{"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*queue","shutdown_timeout":"1s"}}` var reply string @@ -371,6 +427,31 @@ func testConfigSSetConfigFromJSONCoreS(t *testing.T) { } } +func testConfigSReloadConfigCoreSDryRun(t *testing.T) { + cfgStr := `{"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*queue","shutdown_timeout":"1s"}}` + var reply string + if err := configRPC.Call(utils.ConfigSv1ReloadConfig, &config.ReloadArgs{ + Tenant: "cgrates.org", + Path: path.Join(*dataDir, "conf", "samples", "caps_busy"), + Section: config.CoreSCfgJson, + DryRun: true, + }, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected OK received: %s", reply) + } + + var rpl string + if err := configRPC.Call(utils.ConfigSv1GetConfigAsJSON, &config.SectionWithOpts{ + Tenant: "cgrates.org", + Section: config.CoreSCfgJson, + }, &rpl); err != nil { + t.Error(err) + } else if cfgStr != rpl { + t.Errorf("Expected %q , received: %q", cfgStr, rpl) + } +} + func testConfigSReloadConfigCoreS(t *testing.T) { cfgStr := `{"cores":{"caps":2,"caps_stats_interval":"0","caps_strategy":"*busy","shutdown_timeout":"1s"}}` var reply string diff --git a/config/config.go b/config/config.go index 764214439..832c65954 100644 --- a/config/config.go +++ b/config/config.go @@ -1541,6 +1541,7 @@ type ReloadArgs struct { Tenant string Path string Section string + DryRun bool } // V1ReloadConfig reloads the configuration @@ -1548,24 +1549,29 @@ func (cfg *CGRConfig) V1ReloadConfig(args *ReloadArgs, reply *string) (err error if missing := utils.MissingStructFields(args, []string{"Path"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if err = cfg.loadCfgWithLocks(args.Path, args.Section); err != nil { + cfgV := cfg + if args.DryRun { + cfgV = cfg.Clone() + } + if err = cfgV.loadCfgWithLocks(args.Path, args.Section); err != nil { return } // lock all sections - cfg.rLockSections() + cfgV.rLockSections() - err = cfg.checkConfigSanity() + err = cfgV.checkConfigSanity() - cfg.rUnlockSections() // unlock before checking the error + cfgV.rUnlockSections() // unlock before checking the error if err != nil { return } - - if args.Section == utils.EmptyString || args.Section == utils.MetaAll { - cfg.reloadSections(sortedCfgSections...) - } else { - cfg.reloadSections(args.Section) + if !args.DryRun { + if args.Section == utils.EmptyString || args.Section == utils.MetaAll { + cfgV.reloadSections(sortedCfgSections...) + } else { + cfgV.reloadSections(args.Section) + } } *reply = utils.OK return @@ -1687,6 +1693,7 @@ type SetConfigArgs struct { Opts map[string]interface{} Tenant string Config map[string]interface{} + DryRun bool } // V1SetConfig reloads the sections of config @@ -1703,20 +1710,25 @@ func (cfg *CGRConfig) V1SetConfig(args *SetConfigArgs, reply *string) (err error if b, err = json.Marshal(args.Config); err != nil { return } - if err = cfg.loadCfgFromJSONWithLocks(bytes.NewBuffer(b), sections); err != nil { + cfgV := cfg + if args.DryRun { + cfgV = cfg.Clone() + } + if err = cfgV.loadCfgFromJSONWithLocks(bytes.NewBuffer(b), sections); err != nil { return } // lock all sections - cfg.rLockSections() + cfgV.rLockSections() - err = cfg.checkConfigSanity() + err = cfgV.checkConfigSanity() - cfg.rUnlockSections() // unlock before checking the error + cfgV.rUnlockSections() // unlock before checking the error if err != nil { return } - - cfg.reloadSections(sections...) + if !args.DryRun { + cfgV.reloadSections(sections...) + } *reply = utils.OK return } @@ -1831,6 +1843,7 @@ type SetConfigFromJSONArgs struct { Opts map[string]interface{} Tenant string Config string + DryRun bool } // V1SetConfigFromJSON reloads the sections of config @@ -1839,20 +1852,24 @@ func (cfg *CGRConfig) V1SetConfigFromJSON(args *SetConfigFromJSONArgs, reply *st *reply = utils.OK return } - - if err = cfg.loadCfgFromJSONWithLocks(bytes.NewBufferString(args.Config), sortedCfgSections); err != nil { + cfgV := cfg + if args.DryRun { + cfgV = cfg.Clone() + } + if err = cfgV.loadCfgFromJSONWithLocks(bytes.NewBufferString(args.Config), sortedCfgSections); err != nil { return } // lock all sections - cfg.rLockSections() - err = cfg.checkConfigSanity() - cfg.rUnlockSections() // unlock before checking the error + cfgV.rLockSections() + err = cfgV.checkConfigSanity() + cfgV.rUnlockSections() // unlock before checking the error if err != nil { return } - - cfg.reloadSections(sortedCfgSections...) + if !args.DryRun { + cfgV.reloadSections(sortedCfgSections...) + } *reply = utils.OK return } diff --git a/config/config_it_test.go b/config/config_it_test.go index 3aa96dfa5..8fd9181e6 100644 --- a/config/config_it_test.go +++ b/config/config_it_test.go @@ -40,6 +40,7 @@ var ( testNewCgrJsonCfgFromHttp, testNewCGRConfigFromPath, testCGRConfigReloadAttributeS, + testCGRConfigReloadChargerSDryRun, testCGRConfigReloadChargerS, testCGRConfigReloadThresholdS, testCGRConfigReloadStatS, @@ -161,6 +162,28 @@ func testCGRConfigReloadAttributeS(t *testing.T) { } } +func testCGRConfigReloadChargerSDryRun(t *testing.T) { + cfg, err := NewDefaultCGRConfig() + if err != nil { + t.Fatal(err) + } + var reply string + if err = cfg.V1ReloadConfig(&ReloadArgs{ + Path: path.Join("/usr", "share", "cgrates", "conf", "samples", "tutmongo2"), + Section: ChargerSCfgJson, + DryRun: true, + }, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected OK received: %s", reply) + } + ecfg, _ := NewDefaultCGRConfig() + + if !reflect.DeepEqual(ecfg.ChargerSCfg(), cfg.ChargerSCfg()) { + t.Errorf("Expected %s , received: %s ", utils.ToJSON(ecfg.ChargerSCfg()), utils.ToJSON(cfg.ChargerSCfg())) + } +} + func testCGRConfigReloadChargerS(t *testing.T) { cfg, err := NewDefaultCGRConfig() if err != nil { diff --git a/config/config_test.go b/config/config_test.go index 737da9aec..a34159f47 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -4699,6 +4699,16 @@ func TestV1GetConfigSectionConfigs(t *testing.T) { } var result string + if cfgCgr2, err := NewDefaultCGRConfig(); err != nil { + t.Error(err) + } else if err = cfgCgr2.V1SetConfig(&SetConfigArgs{Config: reply, DryRun: true}, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Errorf("Unexpected result") + } else if cfgCgr, _ := NewDefaultCGRConfig(); !reflect.DeepEqual(cfgCgr.ConfigSCfg(), cfgCgr2.ConfigSCfg()) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(cfgCgr.ConfigSCfg()), utils.ToJSON(cfgCgr2.ConfigSCfg())) + } + if cfgCgr2, err := NewDefaultCGRConfig(); err != nil { t.Error(err) } else if err = cfgCgr2.V1SetConfig(&SetConfigArgs{Config: reply}, &result); err != nil { @@ -5401,6 +5411,15 @@ func TestV1GetConfigAsJSONCoreS(t *testing.T) { } var result string + if cfgCgr2, err := NewDefaultCGRConfig(); err != nil { + t.Error(err) + } else if err = cfgCgr2.V1SetConfigFromJSON(&SetConfigFromJSONArgs{Config: reply, DryRun: true}, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Errorf("Unexpected result") + } else if cgrCfg, _ := NewDefaultCGRConfig(); !reflect.DeepEqual(cgrCfg.CoreSCfg(), cfgCgr2.CoreSCfg()) { + t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(cgrCfg.CoreSCfg()), utils.ToJSON(cfgCgr2.CoreSCfg())) + } if cfgCgr2, err := NewDefaultCGRConfig(); err != nil { t.Error(err) } else if err = cfgCgr2.V1SetConfigFromJSON(&SetConfigFromJSONArgs{Config: reply}, &result); err != nil {