Updated integration tests

This commit is contained in:
Trial97
2020-12-11 12:28:46 +02:00
committed by Dan Christian Bogos
parent 1bf66311f5
commit 63345c3c85
13 changed files with 159 additions and 107 deletions

View File

@@ -35,6 +35,7 @@ func NewActionS(cfg *config.CGRConfig, fltrS *engine.FilterS, dm *engine.DataMan
cfg: cfg,
fltrS: fltrS,
dm: dm,
crn: cron.New(),
}
}
@@ -50,7 +51,6 @@ type ActionS struct {
func (aS *ActionS) ListenAndServe(stopChan, cfgRld chan struct{}) {
utils.Logger.Info(fmt.Sprintf("<%s> starting <%s>",
utils.CoreS, utils.ActionS))
aS.crn = cron.New()
aS.crn.Start()
for {
select {

View File

@@ -159,7 +159,7 @@ func (aSv1 *ActionSv1) Call(serviceMethod string,
}
// Ping return pong if the service is active
func (aSv1 *ActionSv1) Ping(ign *utils.CGREvent, reply *string) error {
func (aSv1 *ActionSv1) Ping(ign *utils.CGREventWithOpts, reply *string) error {
*reply = utils.Pong
return nil
}

View File

@@ -192,7 +192,7 @@ func testActionSGetActionProfile(t *testing.T) {
func testActionSPing(t *testing.T) {
var resp string
if err := actSRPC.Call(utils.ActionSv1Ping, new(utils.CGREvent), &resp); err != nil {
if err := actSRPC.Call(utils.ActionSv1Ping, new(utils.CGREventWithOpts), &resp); err != nil {
t.Error(err)
} else if resp != utils.Pong {
t.Error("Unexpected reply returned", resp)
@@ -202,7 +202,7 @@ func testActionSPing(t *testing.T) {
func testActionSSettActionProfile(t *testing.T) {
actPrf = &ActionProfileWithCache{
ActionProfileWithOpts: &engine.ActionProfileWithOpts{
&engine.ActionProfile{
ActionProfile: &engine.ActionProfile{
Tenant: "tenant_test",
ID: "id_test",
FilterIDs: nil,
@@ -233,12 +233,13 @@ func testActionSSettActionProfile(t *testing.T) {
},
},
},
map[string]interface{}{},
Opts: map[string]interface{}{},
},
}
var result string
expErr := utils.ErrNotFound.Error()
if err := actSRPC.Call(utils.APIerSv1GetActionProfile, &utils.TenantID{Tenant: "tenant_test", ID: "id_test"}, &result); err == nil || err.Error() != expErr {
if err := actSRPC.Call(utils.APIerSv1GetActionProfile, &utils.TenantIDWithOpts{
TenantID: &utils.TenantID{Tenant: "tenant_test", ID: "id_test"}}, &result); err == nil || err.Error() != expErr {
t.Errorf("Expected error: %v received: %v", expErr, err)
}
var reply string
@@ -248,7 +249,8 @@ func testActionSSettActionProfile(t *testing.T) {
t.Error("Unexpected reply returned", reply)
}
var reply2 *engine.ActionProfile
if err := actSRPC.Call(utils.APIerSv1GetActionProfile, &utils.TenantID{Tenant: "tenant_test", ID: "id_test"}, &reply2); err != nil {
if err := actSRPC.Call(utils.APIerSv1GetActionProfile, &utils.TenantIDWithOpts{
TenantID: &utils.TenantID{Tenant: "tenant_test", ID: "id_test"}}, &reply2); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(actPrf.ActionProfile, reply2) {
t.Errorf("Expecting : %+v, received: %+v", actPrf.ActionProfile, reply2)
@@ -301,7 +303,8 @@ func testActionSUpdateActionProfile(t *testing.T) {
t.Error("Unexpected reply returned", reply)
}
var reply2 *engine.ActionProfile
if err := actSRPC.Call(utils.APIerSv1GetActionProfile, &utils.TenantID{Tenant: "tenant_test", ID: "id_test"}, &reply2); err != nil {
if err := actSRPC.Call(utils.APIerSv1GetActionProfile, &utils.TenantIDWithOpts{
TenantID: &utils.TenantID{Tenant: "tenant_test", ID: "id_test"}}, &reply2); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(actPrf.ActionProfile, reply2) {
t.Errorf("Expecting : %+v, received: %+v", actPrf.ActionProfile, reply2)
@@ -317,7 +320,8 @@ func testActionSRemoveActionProfile(t *testing.T) {
}
var reply2 *engine.ActionProfile
expErr := utils.ErrNotFound.Error()
if err := actSRPC.Call(utils.APIerSv1GetActionProfile, &utils.TenantID{Tenant: "tenant_test", ID: "id_test"}, &reply2); err == nil || err.Error() != expErr {
if err := actSRPC.Call(utils.APIerSv1GetActionProfile, &utils.TenantIDWithOpts{
TenantID: &utils.TenantID{Tenant: "tenant_test", ID: "id_test"}}, &reply2); err == nil || err.Error() != expErr {
t.Errorf("Expected error: %v received: %v", expErr, err)
}
if err := actSRPC.Call(utils.APIerSv1RemoveActionProfile, &utils.TenantIDWithCache{Tenant: "tenant_test", ID: "id_test"}, &reply2); err == nil || err.Error() != expErr {

View File

@@ -760,7 +760,7 @@ func testApierTPAccountActions(t *testing.T) {
// Check missing params
if err := rater.Call(utils.APIerSv1SetTPAccountActions, new(utils.TPAccountActions), &reply); err == nil {
t.Error("Calling APIerSv1.SetTPAccountActions, expected error, received: ", reply)
} else if err.Error() != "MANDATORY_IE_MISSING: [TPid LoadId Tenant Account ActionPlanId]" {
} else if err.Error() != "MANDATORY_IE_MISSING: [TPid LoadId Account ActionPlanId]" {
t.Error("Calling APIerSv1.SetTPAccountActions got unexpected error: ", err.Error())
}
// Test get
@@ -842,7 +842,7 @@ func testApierLoadAccountActions(t *testing.T) {
expectedStats[utils.CacheAccountActionPlans].Items = 1
expectedStats[utils.CacheActionPlans].Items = 1
expectedStats[utils.CacheActions].Items = 1
expectedStats[utils.CacheLoadIDs].Items = 6
expectedStats[utils.CacheLoadIDs].Items = 8
expectedStats[utils.CacheRPCConnections].Items = 1
if err := rater.Call(utils.CacheSv1GetCacheStats, new(utils.AttrCacheIDsWithOpts), &rcvStats); err != nil {
t.Error("Got error on CacheSv1.GetCacheStats: ", err.Error())
@@ -880,7 +880,7 @@ func testApierSetRatingProfile(t *testing.T) {
expectedStats[utils.CacheActions].Items = 1
expectedStats[utils.CacheRatingProfiles].Items = 1
expectedStats[utils.CacheRPCConnections].Items = 1
expectedStats[utils.CacheLoadIDs].Items = 6
expectedStats[utils.CacheLoadIDs].Items = 8
if err := rater.Call(utils.CacheSv1GetCacheStats, new(utils.AttrCacheIDsWithOpts), &rcvStats); err != nil {
t.Error("Got error on CacheSv1.GetCacheStats: ", err.Error())
} else if !reflect.DeepEqual(expectedStats, rcvStats) {
@@ -1031,7 +1031,7 @@ func testApierReloadCache(t *testing.T) {
expectedStats[utils.CacheRatingProfiles].Items = 3
expectedStats[utils.CacheRatingPlans].Items = 1
expectedStats[utils.CacheReverseDestinations].Items = 10
expectedStats[utils.CacheLoadIDs].Items = 6
expectedStats[utils.CacheLoadIDs].Items = 8
expectedStats[utils.CacheRPCConnections].Items = 1
if err := rater.Call(utils.CacheSv1GetCacheStats, new(utils.AttrCacheIDsWithOpts), &rcvStats); err != nil {
t.Error("Got error on CacheSv1.GetCacheStats: ", err.Error())

View File

@@ -402,40 +402,45 @@ type AttrRemoveActionTrigger struct {
UniqueID string
}
func (apierSv1 *APIerSv1) RemoveActionTrigger(attr AttrRemoveActionTrigger, reply *string) error {
func (apierSv1 *APIerSv1) RemoveActionTrigger(attr AttrRemoveActionTrigger, reply *string) (err error) {
if missing := utils.MissingStructFields(&attr, []string{"GroupID"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if attr.UniqueID == "" {
err := apierSv1.DataManager.RemoveActionTriggers(attr.GroupID, utils.NonTransactional)
err = apierSv1.DataManager.RemoveActionTriggers(attr.GroupID, utils.NonTransactional)
if err != nil {
*reply = err.Error()
} else {
*reply = utils.OK
return
}
return err
} else {
atrs, err := apierSv1.DataManager.GetActionTriggers(attr.GroupID, false, utils.NonTransactional)
if err != nil {
*reply = err.Error()
return err
}
var remainingAtrs engine.ActionTriggers
for _, atr := range atrs {
if atr.UniqueID == attr.UniqueID {
continue
}
*reply = utils.OK
return
}
var atrs engine.ActionTriggers
if atrs, err = apierSv1.DataManager.GetActionTriggers(attr.GroupID, false, utils.NonTransactional); err != nil {
return
}
remainingAtrs := make(engine.ActionTriggers, 0, len(atrs))
for _, atr := range atrs {
if atr.UniqueID != attr.UniqueID {
remainingAtrs = append(remainingAtrs, atr)
}
// set the cleared list back
err = apierSv1.DataManager.SetActionTriggers(attr.GroupID, remainingAtrs, utils.NonTransactional)
if err != nil {
*reply = err.Error()
} else {
*reply = utils.OK
}
return err
}
// set the cleared list back
if err = apierSv1.DataManager.SetActionTriggers(attr.GroupID, remainingAtrs, utils.NonTransactional); err != nil {
return
}
// CacheReload
if err = apierSv1.ConnMgr.Call(apierSv1.Config.ApierCfg().CachesConns, nil,
utils.CacheSv1ReloadCache, utils.AttrReloadCacheWithOpts{
ArgsCache: map[string][]string{utils.ActionTriggerIDs: {attr.GroupID}},
}, reply); err != nil {
return
}
// generate a loadID for CacheActionTriggers and store it in database
if err = apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActionTriggers: time.Now().UnixNano()}); err != nil {
return utils.APIErrorHandler(err)
}
*reply = utils.OK
return
}
// SetActionTrigger updates a ActionTrigger
@@ -446,7 +451,7 @@ func (apierSv1 *APIerSv1) SetActionTrigger(attr AttrSetActionTrigger, reply *str
atrs, _ := apierSv1.DataManager.GetActionTriggers(attr.GroupID, false, utils.NonTransactional)
var newAtr *engine.ActionTrigger
if attr.UniqueID != "" {
if attr.UniqueID != utils.EmptyString {
//search for exiting one
for _, atr := range atrs {
if atr.UniqueID == attr.UniqueID {
@@ -473,7 +478,17 @@ func (apierSv1 *APIerSv1) SetActionTrigger(attr AttrSetActionTrigger, reply *str
if err = apierSv1.DataManager.SetActionTriggers(attr.GroupID, atrs, utils.NonTransactional); err != nil {
return
}
//no cache for action triggers
// CacheReload
if err = apierSv1.ConnMgr.Call(apierSv1.Config.ApierCfg().CachesConns, nil,
utils.CacheSv1ReloadCache, utils.AttrReloadCacheWithOpts{
ArgsCache: map[string][]string{utils.ActionTriggerIDs: {attr.GroupID}},
}, reply); err != nil {
return
}
// generate a loadID for CacheActionTriggers and store it in database
if err = apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActionTriggers: time.Now().UnixNano()}); err != nil {
return utils.APIErrorHandler(err)
}
*reply = utils.OK
return
}

View File

@@ -27,7 +27,6 @@
{"address": "127.0.0.1:2032", "transport":"*json"}
],
}
},

View File

@@ -13,57 +13,59 @@
"rpc_conns": {
"conn1": {
"strategy": "*first",
"conns": [{"address": "127.0.0.1:2033", "transport":"*gob"}],
},
"conn2": {
"strategy": "*broadcast",
"strategy": "*broadcast_sync",
"conns": [
{"address": "127.0.0.1:2023", "transport":"*gob"},
{"address": "127.0.0.1:2033", "transport":"*gob"}
],
]
},
"connCache": {
"strategy": "*broadcast_sync",// be brodcast sync in test
"conns": [
{"address": "127.0.0.1:2023", "transport":"*gob"},
{"address": "127.0.0.1:2033", "transport":"*gob"}
]
}
},
"data_db": {
"db_type": "*internal",
"remote_conns": ["conn1"],
"replication_conns": ["conn2"],
"items":{
"*accounts":{"remote":true,"replicate":true},
"*reverse_destinations": {"remote":true,"replicate":true},
"*destinations": {"remote":true,"replicate":true},
"*rating_plans": {"remote":true,"replicate":true},
"*rating_profiles":{"remote":true,"replicate":true},
"*actions":{"remote":true,"replicate":true},
"*action_plans": {"remote":true,"replicate":true},
"*account_action_plans":{"remote":true,"replicate":true},
"*action_triggers":{"remote":true,"replicate":true},
"*shared_groups":{"remote":true,"replicate":true},
"*timings": {"remote":true,"replicate":true},
"*resource_profiles":{"remote":true,"replicate":true},
"*resources":{"remote":true,"replicate":true},
"*statqueue_profiles": {"remote":true,"replicate":true},
"*statqueues": {"remote":true,"replicate":true},
"*threshold_profiles": {"remote":true,"replicate":true},
"*thresholds": {"remote":true,"replicate":true},
"*filters": {"remote":true,"replicate":true},
"*route_profiles":{"remote":true,"replicate":true},
"*attribute_profiles":{"remote":true,"replicate":true},
"*charger_profiles": {"remote":true,"replicate":true},
"*dispatcher_profiles":{"remote":true,"replicate":true},
"*dispatcher_hosts":{"remote":true,"replicate":true},
"*indexes" :{"remote":true,"replicate":true},
"*accounts":{"remote":false,"replicate":true},
"*reverse_destinations": {"remote":false,"replicate":true},
"*destinations": {"remote":false,"replicate":true},
"*rating_plans": {"remote":false,"replicate":true},
"*rating_profiles":{"remote":false,"replicate":true},
"*actions":{"remote":false,"replicate":true},
"*action_plans": {"remote":false,"replicate":true},
"*account_action_plans":{"remote":false,"replicate":true},
"*action_triggers":{"remote":false,"replicate":true},
"*shared_groups":{"remote":false,"replicate":true},
"*timings": {"remote":false,"replicate":true},
"*resource_profiles":{"remote":false,"replicate":true},
"*resources":{"remote":false,"replicate":true},
"*statqueue_profiles": {"remote":false,"replicate":true},
"*statqueues": {"remote":false,"replicate":true},
"*threshold_profiles": {"remote":false,"replicate":true},
"*thresholds": {"remote":false,"replicate":true},
"*filters": {"remote":false,"replicate":true},
"*route_profiles":{"remote":false,"replicate":true},
"*attribute_profiles":{"remote":false,"replicate":true},
"*charger_profiles": {"remote":false,"replicate":true},
"*dispatcher_profiles":{"remote":false,"replicate":true},
"*dispatcher_hosts":{"remote":false,"replicate":true},
"*indexes" :{"remote":false,"replicate":true},
"*rate_profiles":{"remote":false,"replicate":true},
"*load_ids":{"remote":true,"replicate":true},
},
"*load_ids":{"remote":false,"replicate":true},
}
},
"stor_db": {
"db_type": "*internal",
"db_type": "*internal",
},
@@ -85,12 +87,6 @@
},
"stats": {
"enabled": false,
"store_interval": "-1",
},
"schedulers": {
"enabled": true,
},
@@ -99,7 +95,7 @@
"apiers": {
"enabled": true,
"scheduler_conns": ["*internal"],
},
"caches_conns":["connCache"],
}
}

View File

@@ -59,7 +59,7 @@ func TestDspActionSIT(t *testing.T) {
func testDspActPrfPing(t *testing.T) {
var reply string
if err := allEngine.RPC.Call(utils.ActionSv1Ping, new(utils.CGREvent), &reply); err != nil {
if err := allEngine.RPC.Call(utils.ActionSv1Ping, new(utils.CGREventWithOpts), &reply); err != nil {
t.Error(err)
} else if reply != utils.Pong {
t.Errorf("Received: %s", reply)

View File

@@ -182,7 +182,8 @@ func (ec *EventCost) ratingIDForRateInterval(ri *RateInterval, rf RatingMatchedF
MaxCostStrategy: ri.Rating.MaxCostStrategy,
TimingID: tmID,
RatesID: rtUUID,
RatingFiltersID: rfUUID})
RatingFiltersID: rfUUID,
})
}
func (ec *EventCost) rateIntervalForRatingID(ratingID string) (ri *RateInterval) {

View File

@@ -31,15 +31,18 @@ import (
A unit in which a call will be split that has a specific price related interval attached to it.
*/
type TimeSpan struct {
TimeStart, TimeEnd time.Time
Cost float64
RateInterval *RateInterval
DurationIndex time.Duration // the call duration so far till TimeEnd
Increments Increments
RoundIncrement *Increment
MatchedSubject, MatchedPrefix, MatchedDestId, RatingPlanId string
CompressFactor int
ratingInfo *RatingInfo
TimeStart, TimeEnd time.Time
Cost float64
RateInterval *RateInterval
DurationIndex time.Duration // the call duration so far till TimeEnd
Increments Increments
RoundIncrement *Increment
MatchedSubject string
MatchedPrefix string
MatchedDestId string
RatingPlanId string
CompressFactor int
ratingInfo *RatingInfo
}
type Increment struct {

View File

@@ -894,10 +894,19 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions)
actionIDs = append(actionIDs, atr.ActionsID)
}
// write action triggers
err = tpr.dm.SetActionTriggers(accountAction.ActionTriggersId, actionTriggers, utils.NonTransactional)
if err != nil {
if err = tpr.dm.SetActionTriggers(accountAction.ActionTriggersId,
actionTriggers, utils.NonTransactional); err != nil {
return errors.New(err.Error() + " (SetActionTriggers): " + accountAction.ActionTriggersId)
}
var reply string
if err := connMgr.Call(tpr.cacheConns, nil,
utils.CacheSv1ReloadCache, utils.AttrReloadCacheWithOpts{
ArgsCache: map[string][]string{
utils.ActionTriggerIDs: {accountAction.ActionTriggersId},
},
}, &reply); err != nil {
return err
}
}
// actions
@@ -1007,7 +1016,13 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions)
if err = tpr.dm.SetActions(k, as, utils.NonTransactional); err != nil {
return err
}
if err = dm.CacheDataFromDB(utils.ACTION_PREFIX, []string{k}, true); err != nil {
var reply string
if err := connMgr.Call(tpr.cacheConns, nil,
utils.CacheSv1ReloadCache, utils.AttrReloadCacheWithOpts{
ArgsCache: map[string][]string{
utils.ActionIDs: {k},
},
}, &reply); err != nil {
return err
}
}

View File

@@ -248,6 +248,9 @@ func testExpVerifyResources(t *testing.T) {
Weight: 10,
ThresholdIDs: []string{},
}
if *encoding == utils.MetaGOB {
rPrf.ThresholdIDs = nil
}
var reply *engine.ResourceProfile
if err := expRpc.Call(utils.APIerSv1GetResourceProfile,
&utils.TenantID{Tenant: "cgrates.org", ID: "RES_ACNT_1001"}, &reply); err != nil {
@@ -392,8 +395,11 @@ func testExpVerifyRateProfiles(t *testing.T) {
},
}
if err := expRpc.Call(utils.APIerSv1GetRateProfile,
&utils.TenantID{Tenant: "cgrates.org", ID: "RT_SPECIAL_1002"}, &reply); err != nil {
if *encoding == utils.MetaGOB {
splPrf.FilterIDs = nil
}
if err := expRpc.Call(utils.APIerSv1GetRateProfile, &utils.TenantIDWithOpts{
TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RT_SPECIAL_1002"}}, &reply); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(splPrf, reply) {
@@ -412,7 +418,7 @@ func testExpVerifyActionProfiles(t *testing.T) {
Schedule: utils.ASAP,
AccountIDs: utils.StringSet{"1001": {}, "1002": {}},
Actions: []*engine.APAction{
&engine.APAction{
{
ID: "TOPUP",
FilterIDs: []string{},
Type: utils.TOPUP,
@@ -420,28 +426,28 @@ func testExpVerifyActionProfiles(t *testing.T) {
Value: config.NewRSRParsersMustCompile("10", utils.INFIELD_SEP),
},
&engine.APAction{
{
ID: "SET_BALANCE_TEST_DATA",
FilterIDs: []string{},
Type: utils.SET_BALANCE,
Path: utils.DynamicDataPrefix + utils.COUNTER_BALANCE + utils.NestingSep + "TestDataBalance" + utils.NestingSep + utils.Type,
Value: config.NewRSRParsersMustCompile(utils.DATA, utils.INFIELD_SEP),
},
&engine.APAction{
{
ID: "TOPUP_TEST_DATA",
FilterIDs: []string{},
Type: utils.TOPUP,
Path: utils.DynamicDataPrefix + utils.COUNTER_BALANCE + utils.NestingSep + "TestDataBalance" + utils.NestingSep + utils.Value,
Value: config.NewRSRParsersMustCompile("1024", utils.INFIELD_SEP),
},
&engine.APAction{
{
ID: "SET_BALANCE_TEST_VOICE",
FilterIDs: []string{},
Type: utils.SET_BALANCE,
Path: utils.DynamicDataPrefix + utils.COUNTER_BALANCE + utils.NestingSep + "TestVoiceBalance" + utils.NestingSep + utils.Type,
Value: config.NewRSRParsersMustCompile(utils.VOICE, utils.INFIELD_SEP),
},
&engine.APAction{
{
ID: "TOPUP_TEST_VOICE",
FilterIDs: []string{},
Type: utils.TOPUP,
@@ -450,9 +456,14 @@ func testExpVerifyActionProfiles(t *testing.T) {
},
},
}
if err := expRpc.Call(utils.APIerSv1GetActionProfile,
&utils.TenantID{Tenant: "cgrates.org", ID: "ONE_TIME_ACT"}, &reply); err != nil {
if *encoding == utils.MetaGOB {
actPrf.FilterIDs = nil
for _, act := range actPrf.Actions {
act.FilterIDs = nil
}
}
if err := expRpc.Call(utils.APIerSv1GetActionProfile, &utils.TenantIDWithOpts{
TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ONE_TIME_ACT"}}, &reply); err != nil {
t.Fatal(err)
} else {
for _, act := range reply.Actions { // the path variable from RSRParsers is with lower letter and need to be compiled manually in tests to pass reflect.DeepEqual

View File

@@ -47,6 +47,7 @@ func NewActionService(cfg *config.CGRConfig, dm *DataDBService,
server: server,
anz: anz,
srvDep: srvDep,
rldChan: make(chan struct{}),
}
}
@@ -59,6 +60,9 @@ type ActionService struct {
filterSChan chan *engine.FilterS
server *cores.Server
rldChan chan struct{}
stopChan chan struct{}
acts *actions.ActionS
rpc *v1.ActionSv1 // useful on restart
connChan chan rpcclient.ClientConnector // publish the internal Subsystem when available
@@ -84,6 +88,8 @@ func (acts *ActionService) Start() (err error) {
acts.Lock()
defer acts.Unlock()
acts.acts = actions.NewActionS(acts.cfg, filterS, datadb)
acts.stopChan = make(chan struct{})
go acts.acts.ListenAndServe(acts.stopChan, acts.rldChan)
utils.Logger.Info(fmt.Sprintf("<%s> starting <%s> subsystem", utils.CoreS, utils.AttributeS))
acts.rpc = v1.NewActionSv1(acts.acts)
@@ -96,6 +102,7 @@ func (acts *ActionService) Start() (err error) {
// Reload handles the change of config
func (acts *ActionService) Reload() (err error) {
acts.rldChan <- struct{}{}
return // for the moment nothing to reload
}
@@ -103,6 +110,7 @@ func (acts *ActionService) Reload() (err error) {
func (acts *ActionService) Shutdown() (err error) {
acts.Lock()
defer acts.Unlock()
close(acts.stopChan)
if err = acts.acts.Shutdown(); err != nil {
return
}