ソースを参照

break in smaller functions cscli hub, hubtest, notifications, parsers, scenarios, simulation (#2004)

mmetc 2 年 前
コミット
4bffc0df21

+ 18 - 5
cmd/crowdsec-cli/hub.go

@@ -11,7 +11,6 @@ import (
 )
 
 func NewHubCmd() *cobra.Command {
-	/* ---- HUB COMMAND */
 	var cmdHub = &cobra.Command{
 		Use:   "hub [action]",
 		Short: "Manage Hub",
@@ -37,6 +36,14 @@ cscli hub update # Download list of available configurations from the hub
 	}
 	cmdHub.PersistentFlags().StringVarP(&cwhub.HubBranch, "branch", "b", "", "Use given branch from hub")
 
+	cmdHub.AddCommand(NewHubListCmd())
+	cmdHub.AddCommand(NewHubUpdateCmd())
+	cmdHub.AddCommand(NewHubUpgradeCmd())
+
+	return cmdHub
+}
+
+func NewHubListCmd() *cobra.Command {
 	var cmdHubList = &cobra.Command{
 		Use:               "list [-a]",
 		Short:             "List installed configs",
@@ -63,8 +70,11 @@ cscli hub update # Download list of available configurations from the hub
 		},
 	}
 	cmdHubList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well")
-	cmdHub.AddCommand(cmdHubList)
 
+	return cmdHubList
+}
+
+func NewHubUpdateCmd() *cobra.Command {
 	var cmdHubUpdate = &cobra.Command{
 		Use:   "update",
 		Short: "Fetch available configs from hub",
@@ -97,8 +107,11 @@ Fetches the [.index.json](https://github.com/crowdsecurity/hub/blob/master/.inde
 			}
 		},
 	}
-	cmdHub.AddCommand(cmdHubUpdate)
 
+	return cmdHubUpdate
+}
+
+func NewHubUpgradeCmd() *cobra.Command {
 	var cmdHubUpgrade = &cobra.Command{
 		Use:   "upgrade",
 		Short: "Upgrade all configs installed from hub",
@@ -137,6 +150,6 @@ Upgrade all configs installed from Crowdsec Hub. Run 'sudo cscli hub update' if
 		},
 	}
 	cmdHubUpgrade.PersistentFlags().BoolVar(&forceAction, "force", false, "Force upgrade : Overwrite tainted and outdated files")
-	cmdHub.AddCommand(cmdHubUpgrade)
-	return cmdHub
+
+	return cmdHubUpgrade
 }

+ 53 - 11
cmd/crowdsec-cli/hubtest.go

@@ -23,9 +23,7 @@ var (
 )
 
 func NewHubTestCmd() *cobra.Command {
-	/* ---- HUB COMMAND */
 	var hubPath string
-	var logType string
 	var crowdsecPath string
 	var cscliPath string
 
@@ -47,11 +45,26 @@ func NewHubTestCmd() *cobra.Command {
 	cmdHubTest.PersistentFlags().StringVar(&crowdsecPath, "crowdsec", "crowdsec", "Path to crowdsec")
 	cmdHubTest.PersistentFlags().StringVar(&cscliPath, "cscli", "cscli", "Path to cscli")
 
+	cmdHubTest.AddCommand(NewHubTestCreateCmd())
+	cmdHubTest.AddCommand(NewHubTestRunCmd())
+	cmdHubTest.AddCommand(NewHubTestCleanCmd())
+	cmdHubTest.AddCommand(NewHubTestInfoCmd())
+	cmdHubTest.AddCommand(NewHubTestListCmd())
+	cmdHubTest.AddCommand(NewHubTestCoverageCmd())
+	cmdHubTest.AddCommand(NewHubTestEvalCmd())
+	cmdHubTest.AddCommand(NewHubTestExplainCmd())
+
+	return cmdHubTest
+}
+
+
+func NewHubTestCreateCmd() *cobra.Command {
 	parsers := []string{}
 	postoverflows := []string{}
 	scenarios := []string{}
 	var ignoreParsers bool
 	var labels map[string]string
+	var logType string
 
 	var cmdHubTestCreate = &cobra.Command{
 		Use:   "create",
@@ -153,11 +166,16 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
 	cmdHubTestCreate.Flags().StringSliceVar(&postoverflows, "postoverflows", postoverflows, "Postoverflows to add to test")
 	cmdHubTestCreate.Flags().StringSliceVarP(&scenarios, "scenarios", "s", scenarios, "Scenarios to add to test")
 	cmdHubTestCreate.PersistentFlags().BoolVar(&ignoreParsers, "ignore-parsers", false, "Don't run test on parsers")
-	cmdHubTest.AddCommand(cmdHubTestCreate)
 
+	return cmdHubTestCreate
+}
+
+
+func NewHubTestRunCmd() *cobra.Command {
 	var noClean bool
 	var runAll bool
 	var forceClean bool
+
 	var cmdHubTestRun = &cobra.Command{
 		Use:               "run",
 		Short:             "run [test_name]",
@@ -300,8 +318,12 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
 	cmdHubTestRun.Flags().BoolVar(&noClean, "no-clean", false, "Don't clean runtime environment if test succeed")
 	cmdHubTestRun.Flags().BoolVar(&forceClean, "clean", false, "Clean runtime environment if test fail")
 	cmdHubTestRun.Flags().BoolVar(&runAll, "all", false, "Run all tests")
-	cmdHubTest.AddCommand(cmdHubTestRun)
 
+	return cmdHubTestRun
+}
+
+
+func NewHubTestCleanCmd() *cobra.Command {
 	var cmdHubTestClean = &cobra.Command{
 		Use:               "clean",
 		Short:             "clean [test_name]",
@@ -319,8 +341,12 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
 			}
 		},
 	}
-	cmdHubTest.AddCommand(cmdHubTestClean)
 
+	return cmdHubTestClean
+}
+
+
+func NewHubTestInfoCmd() *cobra.Command {
 	var cmdHubTestInfo = &cobra.Command{
 		Use:               "info",
 		Short:             "info [test_name]",
@@ -342,8 +368,12 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
 			}
 		},
 	}
-	cmdHubTest.AddCommand(cmdHubTestInfo)
 
+	return cmdHubTestInfo
+}
+
+
+func NewHubTestListCmd() *cobra.Command {
 	var cmdHubTestList = &cobra.Command{
 		Use:               "list",
 		Short:             "list",
@@ -367,11 +397,16 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
 			}
 		},
 	}
-	cmdHubTest.AddCommand(cmdHubTestList)
 
+	return cmdHubTestList
+}
+
+
+func NewHubTestCoverageCmd() *cobra.Command {
 	var showParserCov bool
 	var showScenarioCov bool
 	var showOnlyPercent bool
+
 	var cmdHubTestCoverage = &cobra.Command{
 		Use:               "coverage",
 		Short:             "coverage",
@@ -463,8 +498,12 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
 	cmdHubTestCoverage.PersistentFlags().BoolVar(&showOnlyPercent, "percent", false, "Show only percentages of coverage")
 	cmdHubTestCoverage.PersistentFlags().BoolVar(&showParserCov, "parsers", false, "Show only parsers coverage")
 	cmdHubTestCoverage.PersistentFlags().BoolVar(&showScenarioCov, "scenarios", false, "Show only scenarios coverage")
-	cmdHubTest.AddCommand(cmdHubTestCoverage)
 
+	return cmdHubTestCoverage
+}
+
+
+func NewHubTestEvalCmd() *cobra.Command {
 	var evalExpression string
 	var cmdHubTestEval = &cobra.Command{
 		Use:               "eval",
@@ -490,8 +529,12 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
 		},
 	}
 	cmdHubTestEval.PersistentFlags().StringVarP(&evalExpression, "expr", "e", "", "Expression to eval")
-	cmdHubTest.AddCommand(cmdHubTestEval)
 
+	return cmdHubTestEval
+}
+
+
+func NewHubTestExplainCmd() *cobra.Command {
 	var cmdHubTestExplain = &cobra.Command{
 		Use:               "explain",
 		Short:             "explain [test_name]",
@@ -531,7 +574,6 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
 			}
 		},
 	}
-	cmdHubTest.AddCommand(cmdHubTestExplain)
 
-	return cmdHubTest
+	return cmdHubTestExplain
 }

+ 89 - 66
cmd/crowdsec-cli/notifications.go

@@ -27,12 +27,14 @@ import (
 	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 )
 
+
 type NotificationsCfg struct {
 	Config   csplugin.PluginConfig  `json:"plugin_config"`
 	Profiles []*csconfig.ProfileCfg `json:"associated_profiles"`
 	ids      []uint
 }
 
+
 func NewNotificationsCmd() *cobra.Command {
 	var cmdNotifications = &cobra.Command{
 		Use:               "notifications [action]",
@@ -54,6 +56,80 @@ func NewNotificationsCmd() *cobra.Command {
 		},
 	}
 
+
+	cmdNotifications.AddCommand(NewNotificationsListCmd())
+	cmdNotifications.AddCommand(NewNotificationsInspectCmd())
+	cmdNotifications.AddCommand(NewNotificationsReinjectCmd())
+
+	return cmdNotifications
+}
+
+
+func getNotificationsConfiguration() (map[string]NotificationsCfg, error) {
+	pcfgs := map[string]csplugin.PluginConfig{}
+	wf := func(path string, info fs.FileInfo, err error) error {
+		if info == nil {
+			return errors.Wrapf(err, "error while traversing directory %s", path)
+		}
+		name := filepath.Join(csConfig.ConfigPaths.NotificationDir, info.Name()) //Avoid calling info.Name() twice
+		if (strings.HasSuffix(name, "yaml") || strings.HasSuffix(name, "yml")) && !(info.IsDir()) {
+			ts, err := csplugin.ParsePluginConfigFile(name)
+			if err != nil {
+				return errors.Wrapf(err, "Loading notifification plugin configuration with %s", name)
+			}
+			for _, t := range ts {
+				pcfgs[t.Name] = t
+			}
+		}
+		return nil
+	}
+
+	if err := filepath.Walk(csConfig.ConfigPaths.NotificationDir, wf); err != nil {
+		return nil, errors.Wrap(err, "Loading notifification plugin configuration")
+	}
+
+	// A bit of a tricky stuf now: reconcile profiles and notification plugins
+	ncfgs := map[string]NotificationsCfg{}
+	profiles, err := csprofiles.NewProfile(csConfig.API.Server.Profiles)
+	if err != nil {
+		return nil, errors.Wrap(err, "Cannot extract profiles from configuration")
+	}
+	for profileID, profile := range profiles {
+	loop:
+		for _, notif := range profile.Cfg.Notifications {
+			for name, pc := range pcfgs {
+				if notif == name {
+					if _, ok := ncfgs[pc.Name]; !ok {
+						ncfgs[pc.Name] = NotificationsCfg{
+							Config:   pc,
+							Profiles: []*csconfig.ProfileCfg{profile.Cfg},
+							ids:      []uint{uint(profileID)},
+						}
+						continue loop
+					}
+					tmp := ncfgs[pc.Name]
+					for _, pr := range tmp.Profiles {
+						var profiles []*csconfig.ProfileCfg
+						if pr.Name == profile.Cfg.Name {
+							continue
+						}
+						profiles = append(tmp.Profiles, profile.Cfg)
+						ids := append(tmp.ids, uint(profileID))
+						ncfgs[pc.Name] = NotificationsCfg{
+							Config:   tmp.Config,
+							Profiles: profiles,
+							ids:      ids,
+						}
+					}
+				}
+			}
+		}
+	}
+	return ncfgs, nil
+}
+
+
+func NewNotificationsListCmd() *cobra.Command {
 	var cmdNotificationsList = &cobra.Command{
 		Use:               "list",
 		Short:             "List active notifications plugins",
@@ -96,8 +172,12 @@ func NewNotificationsCmd() *cobra.Command {
 			return nil
 		},
 	}
-	cmdNotifications.AddCommand(cmdNotificationsList)
 
+	return cmdNotificationsList
+}
+
+
+func NewNotificationsInspectCmd() *cobra.Command {
 	var cmdNotificationsInspect = &cobra.Command{
 		Use:               "inspect",
 		Short:             "Inspect active notifications plugin configuration",
@@ -142,9 +222,15 @@ func NewNotificationsCmd() *cobra.Command {
 			return nil
 		},
 	}
-	cmdNotifications.AddCommand(cmdNotificationsInspect)
+
+	return cmdNotificationsInspect
+}
+
+
+func NewNotificationsReinjectCmd() *cobra.Command {
 	var remediation bool
 	var alertOverride string
+
 	var cmdNotificationsReinject = &cobra.Command{
 		Use:   "reinject",
 		Short: "reinject alert into notifications system",
@@ -264,69 +350,6 @@ cscli notifications reinject <alert_id> -a '{"remediation": true,"scenario":"not
 	}
 	cmdNotificationsReinject.Flags().BoolVarP(&remediation, "remediation", "r", false, "Set Alert.Remediation to false in the reinjected alert (see your profile filter configuration)")
 	cmdNotificationsReinject.Flags().StringVarP(&alertOverride, "alert", "a", "", "JSON string used to override alert fields in the reinjected alert (see crowdsec/pkg/models/alert.go in the source tree for the full definition of the object)")
-	cmdNotifications.AddCommand(cmdNotificationsReinject)
-	return cmdNotifications
-}
-
-func getNotificationsConfiguration() (map[string]NotificationsCfg, error) {
-	pcfgs := map[string]csplugin.PluginConfig{}
-	wf := func(path string, info fs.FileInfo, err error) error {
-		if info == nil {
-			return errors.Wrapf(err, "error while traversing directory %s", path)
-		}
-		name := filepath.Join(csConfig.ConfigPaths.NotificationDir, info.Name()) //Avoid calling info.Name() twice
-		if (strings.HasSuffix(name, "yaml") || strings.HasSuffix(name, "yml")) && !(info.IsDir()) {
-			ts, err := csplugin.ParsePluginConfigFile(name)
-			if err != nil {
-				return errors.Wrapf(err, "Loading notifification plugin configuration with %s", name)
-			}
-			for _, t := range ts {
-				pcfgs[t.Name] = t
-			}
-		}
-		return nil
-	}
 
-	if err := filepath.Walk(csConfig.ConfigPaths.NotificationDir, wf); err != nil {
-		return nil, errors.Wrap(err, "Loading notifification plugin configuration")
-	}
-
-	// A bit of a tricky stuf now: reconcile profiles and notification plugins
-	ncfgs := map[string]NotificationsCfg{}
-	profiles, err := csprofiles.NewProfile(csConfig.API.Server.Profiles)
-	if err != nil {
-		return nil, errors.Wrap(err, "Cannot extract profiles from configuration")
-	}
-	for profileID, profile := range profiles {
-	loop:
-		for _, notif := range profile.Cfg.Notifications {
-			for name, pc := range pcfgs {
-				if notif == name {
-					if _, ok := ncfgs[pc.Name]; !ok {
-						ncfgs[pc.Name] = NotificationsCfg{
-							Config:   pc,
-							Profiles: []*csconfig.ProfileCfg{profile.Cfg},
-							ids:      []uint{uint(profileID)},
-						}
-						continue loop
-					}
-					tmp := ncfgs[pc.Name]
-					for _, pr := range tmp.Profiles {
-						var profiles []*csconfig.ProfileCfg
-						if pr.Name == profile.Cfg.Name {
-							continue
-						}
-						profiles = append(tmp.Profiles, profile.Cfg)
-						ids := append(tmp.ids, uint(profileID))
-						ncfgs[pc.Name] = NotificationsCfg{
-							Config:   tmp.Config,
-							Profiles: profiles,
-							ids:      ids,
-						}
-					}
-				}
-			}
-		}
-	}
-	return ncfgs, nil
+	return cmdNotificationsReinject
 }

+ 34 - 6
cmd/crowdsec-cli/parsers.go

@@ -10,6 +10,7 @@ import (
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
 )
 
+
 func NewParsersCmd() *cobra.Command {
 	var cmdParsers = &cobra.Command{
 		Use:   "parsers [action] [config]",
@@ -49,7 +50,19 @@ cscli parsers remove crowdsecurity/sshd-logs
 		},
 	}
 
+	cmdParsers.AddCommand(NewParsersInstallCmd())
+	cmdParsers.AddCommand(NewParsersRemoveCmd())
+	cmdParsers.AddCommand(NewParsersUpgradeCmd())
+	cmdParsers.AddCommand(NewParsersInspectCmd())
+	cmdParsers.AddCommand(NewParsersListCmd())
+
+	return cmdParsers
+}
+
+
+func NewParsersInstallCmd() *cobra.Command {
 	var ignoreError bool
+
 	var cmdParsersInstall = &cobra.Command{
 		Use:               "install [config]",
 		Short:             "Install given parser(s)",
@@ -81,8 +94,12 @@ cscli parsers remove crowdsecurity/sshd-logs
 	cmdParsersInstall.PersistentFlags().BoolVarP(&downloadOnly, "download-only", "d", false, "Only download packages, don't enable")
 	cmdParsersInstall.PersistentFlags().BoolVar(&forceAction, "force", false, "Force install : Overwrite tainted and outdated files")
 	cmdParsersInstall.PersistentFlags().BoolVar(&ignoreError, "ignore", false, "Ignore errors when installing multiple parsers")
-	cmdParsers.AddCommand(cmdParsersInstall)
 
+	return cmdParsersInstall
+}
+
+
+func NewParsersRemoveCmd() *cobra.Command {
 	var cmdParsersRemove = &cobra.Command{
 		Use:               "remove [config]",
 		Short:             "Remove given parser(s)",
@@ -111,8 +128,12 @@ cscli parsers remove crowdsecurity/sshd-logs
 	cmdParsersRemove.PersistentFlags().BoolVar(&purge, "purge", false, "Delete source file too")
 	cmdParsersRemove.PersistentFlags().BoolVar(&forceAction, "force", false, "Force remove : Remove tainted and outdated files")
 	cmdParsersRemove.PersistentFlags().BoolVar(&all, "all", false, "Delete all the parsers")
-	cmdParsers.AddCommand(cmdParsersRemove)
 
+	return cmdParsersRemove
+}
+
+
+func NewParsersUpgradeCmd() *cobra.Command {
 	var cmdParsersUpgrade = &cobra.Command{
 		Use:               "upgrade [config]",
 		Short:             "Upgrade given parser(s)",
@@ -137,8 +158,12 @@ cscli parsers remove crowdsecurity/sshd-logs
 	}
 	cmdParsersUpgrade.PersistentFlags().BoolVar(&all, "all", false, "Upgrade all the parsers")
 	cmdParsersUpgrade.PersistentFlags().BoolVar(&forceAction, "force", false, "Force upgrade : Overwrite tainted and outdated files")
-	cmdParsers.AddCommand(cmdParsersUpgrade)
 
+	return cmdParsersUpgrade
+}
+
+
+func NewParsersInspectCmd() *cobra.Command {
 	var cmdParsersInspect = &cobra.Command{
 		Use:               "inspect [name]",
 		Short:             "Inspect given parser",
@@ -154,8 +179,12 @@ cscli parsers remove crowdsecurity/sshd-logs
 		},
 	}
 	cmdParsersInspect.PersistentFlags().StringVarP(&prometheusURL, "url", "u", "", "Prometheus url")
-	cmdParsers.AddCommand(cmdParsersInspect)
 
+	return cmdParsersInspect
+}
+
+
+func NewParsersListCmd() *cobra.Command {
 	var cmdParsersList = &cobra.Command{
 		Use:   "list [name]",
 		Short: "List all parsers or given one",
@@ -168,7 +197,6 @@ cscli parser list crowdsecurity/xxx`,
 		},
 	}
 	cmdParsersList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well")
-	cmdParsers.AddCommand(cmdParsersList)
 
-	return cmdParsers
+	return cmdParsersList
 }

+ 28 - 7
cmd/crowdsec-cli/scenarios.go

@@ -12,7 +12,6 @@ import (
 )
 
 func NewScenariosCmd() *cobra.Command {
-
 	var cmdScenarios = &cobra.Command{
 		Use:   "scenarios [action] [config]",
 		Short: "Install/Remove/Upgrade/Inspect scenario(s) from hub",
@@ -52,7 +51,18 @@ cscli scenarios remove crowdsecurity/ssh-bf
 		},
 	}
 
+	cmdScenarios.AddCommand(NewCmdScenariosInstall())
+	cmdScenarios.AddCommand(NewCmdScenariosRemove())
+	cmdScenarios.AddCommand(NewCmdScenariosUpgrade())
+	cmdScenarios.AddCommand(NewCmdScenariosInspect())
+	cmdScenarios.AddCommand(NewCmdScenariosList())
+
+	return cmdScenarios
+}
+
+func NewCmdScenariosInstall() *cobra.Command {
 	var ignoreError bool
+
 	var cmdScenariosInstall = &cobra.Command{
 		Use:     "install [config]",
 		Short:   "Install given scenario(s)",
@@ -84,8 +94,11 @@ cscli scenarios remove crowdsecurity/ssh-bf
 	cmdScenariosInstall.PersistentFlags().BoolVarP(&downloadOnly, "download-only", "d", false, "Only download packages, don't enable")
 	cmdScenariosInstall.PersistentFlags().BoolVar(&forceAction, "force", false, "Force install : Overwrite tainted and outdated files")
 	cmdScenariosInstall.PersistentFlags().BoolVar(&ignoreError, "ignore", false, "Ignore errors when installing multiple scenarios")
-	cmdScenarios.AddCommand(cmdScenariosInstall)
 
+	return cmdScenariosInstall
+}
+
+func NewCmdScenariosRemove() *cobra.Command {
 	var cmdScenariosRemove = &cobra.Command{
 		Use:     "remove [config]",
 		Short:   "Remove given scenario(s)",
@@ -114,8 +127,11 @@ cscli scenarios remove crowdsecurity/ssh-bf
 	cmdScenariosRemove.PersistentFlags().BoolVar(&purge, "purge", false, "Delete source file too")
 	cmdScenariosRemove.PersistentFlags().BoolVar(&forceAction, "force", false, "Force remove : Remove tainted and outdated files")
 	cmdScenariosRemove.PersistentFlags().BoolVar(&all, "all", false, "Delete all the scenarios")
-	cmdScenarios.AddCommand(cmdScenariosRemove)
 
+	return cmdScenariosRemove
+}
+
+func NewCmdScenariosUpgrade() *cobra.Command {
 	var cmdScenariosUpgrade = &cobra.Command{
 		Use:     "upgrade [config]",
 		Short:   "Upgrade given scenario(s)",
@@ -140,8 +156,11 @@ cscli scenarios remove crowdsecurity/ssh-bf
 	}
 	cmdScenariosUpgrade.PersistentFlags().BoolVarP(&all, "all", "a", false, "Upgrade all the scenarios")
 	cmdScenariosUpgrade.PersistentFlags().BoolVar(&forceAction, "force", false, "Force upgrade : Overwrite tainted and outdated files")
-	cmdScenarios.AddCommand(cmdScenariosUpgrade)
 
+	return cmdScenariosUpgrade
+}
+
+func NewCmdScenariosInspect() *cobra.Command {
 	var cmdScenariosInspect = &cobra.Command{
 		Use:     "inspect [config]",
 		Short:   "Inspect given scenario",
@@ -157,8 +176,11 @@ cscli scenarios remove crowdsecurity/ssh-bf
 		},
 	}
 	cmdScenariosInspect.PersistentFlags().StringVarP(&prometheusURL, "url", "u", "", "Prometheus url")
-	cmdScenarios.AddCommand(cmdScenariosInspect)
 
+	return cmdScenariosInspect
+}
+
+func NewCmdScenariosList() *cobra.Command {
 	var cmdScenariosList = &cobra.Command{
 		Use:   "list [config]",
 		Short: "List all scenario(s) or given one",
@@ -171,7 +193,6 @@ cscli scenarios list crowdsecurity/xxx`,
 		},
 	}
 	cmdScenariosList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well")
-	cmdScenarios.AddCommand(cmdScenariosList)
 
-	return cmdScenarios
+	return cmdScenariosList
 }

+ 20 - 4
cmd/crowdsec-cli/simulation.go

@@ -127,7 +127,16 @@ cscli simulation disable crowdsecurity/ssh-bf`,
 	cmdSimulation.Flags().SortFlags = false
 	cmdSimulation.PersistentFlags().SortFlags = false
 
+	cmdSimulation.AddCommand(NewSimulationEnableCmd())
+	cmdSimulation.AddCommand(NewSimulationDisableCmd())
+	cmdSimulation.AddCommand(NewSimulationStatusCmd())
+
+	return cmdSimulation
+}
+
+func NewSimulationEnableCmd() *cobra.Command {
 	var forceGlobalSimulation bool
+
 	var cmdSimulationEnable = &cobra.Command{
 		Use:               "enable [scenario] [-global]",
 		Short:             "Enable the simulation, globally or on specified scenarios",
@@ -186,7 +195,12 @@ cscli simulation disable crowdsecurity/ssh-bf`,
 		},
 	}
 	cmdSimulationEnable.Flags().BoolVarP(&forceGlobalSimulation, "global", "g", false, "Enable global simulation (reverse mode)")
-	cmdSimulation.AddCommand(cmdSimulationEnable)
+
+	return cmdSimulationEnable
+}
+
+func NewSimulationDisableCmd() *cobra.Command {
+	var forceGlobalSimulation bool
 
 	var cmdSimulationDisable = &cobra.Command{
 		Use:               "disable [scenario]",
@@ -230,8 +244,11 @@ cscli simulation disable crowdsecurity/ssh-bf`,
 		},
 	}
 	cmdSimulationDisable.Flags().BoolVarP(&forceGlobalSimulation, "global", "g", false, "Disable global simulation (reverse mode)")
-	cmdSimulation.AddCommand(cmdSimulationDisable)
 
+	return cmdSimulationDisable
+}
+
+func NewSimulationStatusCmd() *cobra.Command {
 	var cmdSimulationStatus = &cobra.Command{
 		Use:               "status",
 		Short:             "Show simulation mode status",
@@ -245,7 +262,6 @@ cscli simulation disable crowdsecurity/ssh-bf`,
 		PersistentPostRun: func(cmd *cobra.Command, args []string) {
 		},
 	}
-	cmdSimulation.AddCommand(cmdSimulationStatus)
 
-	return cmdSimulation
+	return cmdSimulationStatus
 }