Skip to content

Commit

Permalink
refact "cscli" root cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
mmetc committed Feb 5, 2024
1 parent 81acad0 commit 1bbdfc8
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ linters-settings:

nestif:
# lower this after refactoring
min-complexity: 27
min-complexity: 28

nlreturn:
block-size: 4
Expand Down
9 changes: 7 additions & 2 deletions cmd/crowdsec-cli/config_restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,12 @@ func restoreConfigFromDirectory(dirPath string, oldBackup bool) error {
// Now we have config.yaml, we should regenerate config struct to have rights paths etc
ConfigFilePath = fmt.Sprintf("%s/config.yaml", csConfig.ConfigPaths.ConfigDir)

initConfig()
log.Debug("Reloading configuration")

csConfig, _, err = loadConfigFor("config")
if err != nil {
return fmt.Errorf("failed to reload configuration: %s", err)
}

Check warning on line 154 in cmd/crowdsec-cli/config_restore.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/config_restore.go#L153-L154

Added lines #L153 - L154 were not covered by tests

backupCAPICreds := fmt.Sprintf("%s/online_api_credentials.yaml", dirPath)
if _, err = os.Stat(backupCAPICreds); err == nil {
Expand Down Expand Up @@ -227,7 +232,7 @@ func restoreConfigFromDirectory(dirPath string, oldBackup bool) error {
}
}

// if there is files in the acquis backup dir, restore them
// if there are files in the acquis backup dir, restore them
acquisBackupDir := filepath.Join(dirPath, "acquis", "*.yaml")
if acquisFiles, err := filepath.Glob(acquisBackupDir); err == nil {
for _, acquisFile := range acquisFiles {
Expand Down
201 changes: 120 additions & 81 deletions cmd/crowdsec-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,86 @@ import (
"github.com/crowdsecurity/crowdsec/pkg/fflag"
)

var trace_lvl, dbg_lvl, nfo_lvl, wrn_lvl, err_lvl bool

var ConfigFilePath string
var csConfig *csconfig.Config
var dbClient *database.Client

var outputFormat string
var OutputColor string
type configGetter func() *csconfig.Config

var mergedConfig string

// flagBranch overrides the value in csConfig.Cscli.HubBranch
var flagBranch = ""
type cliRoot struct {
logTrace bool
logDebug bool
logInfo bool
logWarn bool
logErr bool
outputColor string
outputFormat string
// flagBranch overrides the value in csConfig.Cscli.HubBranch
flagBranch string
}

type configGetter func() *csconfig.Config
func newCliRoot() *cliRoot {
return &cliRoot{}
}

func initConfig() {
var err error
// cfg() is a helper function to get the configuration loaded from config.yaml,
// we pass it to subcommands because the file is not read until the Execute() call
func (cli *cliRoot) cfg() *csconfig.Config {
return csConfig
}

if trace_lvl {
log.SetLevel(log.TraceLevel)
} else if dbg_lvl {
log.SetLevel(log.DebugLevel)
} else if nfo_lvl {
log.SetLevel(log.InfoLevel)
} else if wrn_lvl {
log.SetLevel(log.WarnLevel)
} else if err_lvl {
log.SetLevel(log.ErrorLevel)
// wantedLogLevel returns the log level requested in the command line flags.
func (cli *cliRoot) wantedLogLevel() log.Level {
switch {
case cli.logTrace:
return log.TraceLevel

Check warning on line 52 in cmd/crowdsec-cli/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/main.go#L51-L52

Added lines #L51 - L52 were not covered by tests
case cli.logDebug:
return log.DebugLevel
case cli.logInfo:
return log.InfoLevel

Check warning on line 56 in cmd/crowdsec-cli/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/main.go#L55-L56

Added lines #L55 - L56 were not covered by tests
case cli.logWarn:
return log.WarnLevel
case cli.logErr:
return log.ErrorLevel
default:
return log.InfoLevel
}
}

// loadConfigFor loads the configuration file for the given sub-command.
// If the sub-command does not need it, it returns a default configuration.
func loadConfigFor(command string) (*csconfig.Config, string, error) {
noNeedConfig := []string{
"doc",
"help",
"completion",
"version",
"hubtest",
}

if !slices.Contains(NoNeedConfig, os.Args[1]) {
if !slices.Contains(noNeedConfig, command) {
log.Debugf("Using %s as configuration file", ConfigFilePath)
csConfig, mergedConfig, err = csconfig.NewConfig(ConfigFilePath, false, false, true)
config, merged, err := csconfig.NewConfig(ConfigFilePath, false, false, true)
if err != nil {
log.Fatal(err)
return nil, "", err
}
} else {
csConfig = csconfig.NewDefaultConfig()
return config, merged, nil
}

return csconfig.NewDefaultConfig(), "", nil
}

// initialize is called before the subcommand is executed.
func (cli *cliRoot) initialize() {
var err error

log.SetLevel(cli.wantedLogLevel())

csConfig, mergedConfig, err = loadConfigFor(os.Args[1])
if err != nil {
log.Fatal(err)
}

// recap of the enabled feature flags, because logging
Expand All @@ -62,12 +103,12 @@ func initConfig() {
log.Debugf("Enabled feature flags: %s", fflist)
}

if flagBranch != "" {
csConfig.Cscli.HubBranch = flagBranch
if cli.flagBranch != "" {
csConfig.Cscli.HubBranch = cli.flagBranch

Check warning on line 107 in cmd/crowdsec-cli/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/main.go#L107

Added line #L107 was not covered by tests
}

if outputFormat != "" {
csConfig.Cscli.Output = outputFormat
if cli.outputFormat != "" {
csConfig.Cscli.Output = cli.outputFormat
}

if csConfig.Cscli.Output == "" {
Expand All @@ -85,11 +126,11 @@ func initConfig() {
log.SetLevel(log.ErrorLevel)
}

if OutputColor != "" {
csConfig.Cscli.Color = OutputColor
if cli.outputColor != "" {
csConfig.Cscli.Color = cli.outputColor

if OutputColor != "yes" && OutputColor != "no" && OutputColor != "auto" {
log.Fatalf("output color %s unknown", OutputColor)
if cli.outputColor != "yes" && cli.outputColor != "no" && cli.outputColor != "auto" {
log.Fatalf("output color %s unknown", cli.outputColor)

Check warning on line 133 in cmd/crowdsec-cli/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/main.go#L133

Added line #L133 was not covered by tests
}
}
}
Expand All @@ -102,15 +143,23 @@ var validArgs = []string{
"postoverflows", "scenarios", "simulation", "support", "version",
}

var NoNeedConfig = []string{
"doc",
"help",
"completion",
"version",
"hubtest",
func (cli *cliRoot) colorize(cmd *cobra.Command) {
cc.Init(&cc.Config{
RootCmd: cmd,
Headings: cc.Yellow,
Commands: cc.Green + cc.Bold,
CmdShortDescr: cc.Cyan,
Example: cc.Italic,
ExecName: cc.Bold,
Aliases: cc.Bold + cc.Italic,
FlagsDataType: cc.White,
Flags: cc.Green,
FlagsDescr: cc.Cyan,
})
cmd.SetOut(color.Output)
}

func main() {
func (cli *cliRoot) NewCommand() *cobra.Command {
// set the formatter asap and worry about level later
logFormatter := &log.TextFormatter{TimestampFormat: time.RFC3339, FullTimestamp: true}
log.SetFormatter(logFormatter)
Expand All @@ -135,31 +184,25 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
/*TBD examples*/
}

cc.Init(&cc.Config{
RootCmd: cmd,
Headings: cc.Yellow,
Commands: cc.Green + cc.Bold,
CmdShortDescr: cc.Cyan,
Example: cc.Italic,
ExecName: cc.Bold,
Aliases: cc.Bold + cc.Italic,
FlagsDataType: cc.White,
Flags: cc.Green,
FlagsDescr: cc.Cyan,
})
cmd.SetOut(color.Output)
cli.colorize(cmd)

/*don't sort flags so we can enforce order*/
cmd.Flags().SortFlags = false

cmd.PersistentFlags().StringVarP(&ConfigFilePath, "config", "c", csconfig.DefaultConfigPath("config.yaml"), "path to crowdsec config file")
cmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "", "Output format: human, json, raw")
cmd.PersistentFlags().StringVarP(&OutputColor, "color", "", "auto", "Output color: yes, no, auto")
cmd.PersistentFlags().BoolVar(&dbg_lvl, "debug", false, "Set logging to debug")
cmd.PersistentFlags().BoolVar(&nfo_lvl, "info", false, "Set logging to info")
cmd.PersistentFlags().BoolVar(&wrn_lvl, "warning", false, "Set logging to warning")
cmd.PersistentFlags().BoolVar(&err_lvl, "error", false, "Set logging to error")
cmd.PersistentFlags().BoolVar(&trace_lvl, "trace", false, "Set logging to trace")
cmd.PersistentFlags().StringVar(&flagBranch, "branch", "", "Override hub branch on github")

if err := cmd.PersistentFlags().MarkHidden("branch"); err != nil {
pflags := cmd.PersistentFlags()
pflags.SortFlags = false

pflags.StringVarP(&ConfigFilePath, "config", "c", csconfig.DefaultConfigPath("config.yaml"), "path to crowdsec config file")
pflags.StringVarP(&cli.outputFormat, "output", "o", "", "Output format: human, json, raw")
pflags.StringVarP(&cli.outputColor, "color", "", "auto", "Output color: yes, no, auto")
pflags.BoolVar(&cli.logDebug, "debug", false, "Set logging to debug")
pflags.BoolVar(&cli.logInfo, "info", false, "Set logging to info")
pflags.BoolVar(&cli.logWarn, "warning", false, "Set logging to warning")
pflags.BoolVar(&cli.logErr, "error", false, "Set logging to error")
pflags.BoolVar(&cli.logTrace, "trace", false, "Set logging to trace")
pflags.StringVar(&cli.flagBranch, "branch", "", "Override hub branch on github")

if err := pflags.MarkHidden("branch"); err != nil {
log.Fatalf("failed to hide flag: %s", err)
}

Expand All @@ -179,29 +222,20 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
}

if len(os.Args) > 1 {
cobra.OnInitialize(initConfig)
}

/*don't sort flags so we can enforce order*/
cmd.Flags().SortFlags = false
cmd.PersistentFlags().SortFlags = false

// we use a getter because the config is not initialized until the Execute() call
getconfig := func() *csconfig.Config {
return csConfig
cobra.OnInitialize(cli.initialize)
}

cmd.AddCommand(NewCLIDoc().NewCommand(cmd))
cmd.AddCommand(NewCLIVersion().NewCommand())
cmd.AddCommand(NewConfigCmd())
cmd.AddCommand(NewCLIHub(getconfig).NewCommand())
cmd.AddCommand(NewCLIMetrics(getconfig).NewCommand())
cmd.AddCommand(NewCLIDashboard(getconfig).NewCommand())
cmd.AddCommand(NewCLIDecisions(getconfig).NewCommand())
cmd.AddCommand(NewCLIHub(cli.cfg).NewCommand())
cmd.AddCommand(NewCLIMetrics(cli.cfg).NewCommand())
cmd.AddCommand(NewCLIDashboard(cli.cfg).NewCommand())
cmd.AddCommand(NewCLIDecisions(cli.cfg).NewCommand())
cmd.AddCommand(NewCLIAlerts().NewCommand())
cmd.AddCommand(NewCLISimulation(getconfig).NewCommand())
cmd.AddCommand(NewCLIBouncers(getconfig).NewCommand())
cmd.AddCommand(NewCLIMachines(getconfig).NewCommand())
cmd.AddCommand(NewCLISimulation(cli.cfg).NewCommand())
cmd.AddCommand(NewCLIBouncers(cli.cfg).NewCommand())
cmd.AddCommand(NewCLIMachines(cli.cfg).NewCommand())
cmd.AddCommand(NewCLICapi().NewCommand())
cmd.AddCommand(NewLapiCmd())
cmd.AddCommand(NewCompletionCmd())
Expand All @@ -210,7 +244,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
cmd.AddCommand(NewCLIHubTest().NewCommand())
cmd.AddCommand(NewCLINotifications().NewCommand())
cmd.AddCommand(NewCLISupport().NewCommand())
cmd.AddCommand(NewCLIPapi(getconfig).NewCommand())
cmd.AddCommand(NewCLIPapi(cli.cfg).NewCommand())
cmd.AddCommand(NewCLICollection().NewCommand())
cmd.AddCommand(NewCLIParser().NewCommand())
cmd.AddCommand(NewCLIScenario().NewCommand())
Expand All @@ -223,6 +257,11 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
cmd.AddCommand(NewSetupCmd())
}

return cmd
}

func main() {
cmd := newCliRoot().NewCommand()
if err := cmd.Execute(); err != nil {
log.Fatal(err)
}
Expand Down

0 comments on commit 1bbdfc8

Please sign in to comment.