-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
new(cmd,pkg,docs,docgen,validate): switched from slog
go library to rich-text falcoctl log library
#333
new(cmd,pkg,docs,docgen,validate): switched from slog
go library to rich-text falcoctl log library
#333
Changes from all commits
2701ea5
8c6a73b
589ad27
1b25d9c
66e37f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ package cmd | |
import ( | ||
"bytes" | ||
"fmt" | ||
"github.com/spf13/pflag" | ||
"os" | ||
"strings" | ||
"text/template" | ||
|
@@ -46,12 +47,12 @@ func validateArgs() cobra.PositionalArgs { | |
if len(args) == 0 { | ||
return nil | ||
} | ||
return cobra.ExactValidArgs(1)(c, args) | ||
return cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs)(c, args) | ||
} | ||
} | ||
|
||
// NewCompletionCmd ... | ||
func NewCompletionCmd() *cobra.Command { | ||
func NewCompletionCmd(_ *ConfigOptions, _ *RootOptions, _ *pflag.FlagSet) *cobra.Command { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same signature as other commands. |
||
var long bytes.Buffer | ||
tmpl := template.Must(template.New("long").Parse(longUsageTemplate)) | ||
tmpl.Execute(&long, map[string]interface{}{ | ||
|
@@ -65,25 +66,23 @@ func NewCompletionCmd() *cobra.Command { | |
Args: validateArgs(), | ||
ValidArgs: cmdArgs, | ||
DisableAutoGenTag: true, | ||
Run: func(c *cobra.Command, args []string) { | ||
RunE: func(c *cobra.Command, args []string) error { | ||
if len(args) == 0 { | ||
c.Help() | ||
return | ||
return c.Help() | ||
} | ||
|
||
arg := args[0] | ||
switch arg { | ||
case "bash": | ||
c.Root().GenBashCompletion(os.Stdout) | ||
break | ||
return c.Root().GenBashCompletion(os.Stdout) | ||
case "zsh": | ||
c.Root().GenZshCompletion(os.Stdout) | ||
break | ||
return c.Root().GenZshCompletion(os.Stdout) | ||
case "fish": | ||
c.Root().GenFishCompletion(os.Stdout, true) | ||
return c.Root().GenFishCompletion(os.Stdout, true) | ||
case "help": | ||
c.Help() | ||
return c.Help() | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,51 +15,143 @@ limitations under the License. | |
package cmd | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"log/slog" | ||
"github.com/falcosecurity/falcoctl/pkg/options" | ||
"github.com/falcosecurity/falcoctl/pkg/output" | ||
"github.com/mitchellh/go-homedir" | ||
"github.com/spf13/pflag" | ||
"github.com/spf13/viper" | ||
"io" | ||
"os" | ||
"strings" | ||
|
||
"github.com/creasty/defaults" | ||
"github.com/falcosecurity/driverkit/validate" | ||
"github.com/go-playground/validator/v10" | ||
"github.com/pterm/pterm" | ||
) | ||
|
||
var validProcessors = []string{"docker", "kubernetes", "kubernetes-in-cluster", "local"} | ||
var aliasProcessors = []string{"docker", "k8s", "k8s-ic"} | ||
var configOptions *ConfigOptions | ||
|
||
// ConfigOptions represent the persistent configuration flags of driverkit. | ||
type ConfigOptions struct { | ||
ConfigFile string | ||
LogLevel string `validate:"loglevel" name:"log level" default:"INFO"` | ||
configFile string | ||
Timeout int `validate:"number,min=30" default:"120" name:"timeout"` | ||
ProxyURL string `validate:"omitempty,proxy" name:"proxy url"` | ||
DryRun bool | ||
dryRun bool | ||
|
||
configErrors bool | ||
// Printer used by all commands to output messages. | ||
Printer *output.Printer | ||
FedeDP marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// writer is used to write the output of the printer. | ||
writer io.Writer | ||
logLevel *options.LogLevel | ||
disableStyling bool | ||
} | ||
|
||
func (co *ConfigOptions) initPrinter() { | ||
// DisableStyling is only enforced by tests. | ||
if co.disableStyling { | ||
pterm.DisableStyling() | ||
} | ||
co.Printer = output.NewPrinter(co.logLevel.ToPtermLogLevel(), pterm.LogFormatterColorful, co.writer) | ||
if co.disableStyling { | ||
// Disable time print for tests | ||
co.Printer.Logger = co.Printer.Logger.WithTime(false) | ||
} | ||
|
||
} | ||
|
||
// Called by tests to disable styling and set bytes buffer as output | ||
func (co *ConfigOptions) setOutput(writer io.Writer, disableStyling bool) { | ||
co.writer = writer | ||
co.disableStyling = disableStyling | ||
co.initPrinter() | ||
} | ||
|
||
// NewConfigOptions creates an instance of ConfigOptions. | ||
func NewConfigOptions() *ConfigOptions { | ||
o := &ConfigOptions{} | ||
func NewConfigOptions() (*ConfigOptions, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When creating a configoptions, default to a INFO level logger. |
||
o := &ConfigOptions{ | ||
writer: os.Stdout, | ||
logLevel: options.NewLogLevel(), | ||
disableStyling: false, | ||
} | ||
o.initPrinter() | ||
if err := defaults.Set(o); err != nil { | ||
slog.With("err", err.Error(), "options", "ConfigOptions").Error("error setting driverkit options defaults") | ||
os.Exit(1) | ||
// Return ConfigOptions anyway because we need the logger | ||
return o, err | ||
} | ||
return o | ||
return o, nil | ||
} | ||
|
||
// Validate validates the ConfigOptions fields. | ||
func (co *ConfigOptions) Validate() []error { | ||
func (co *ConfigOptions) validate() []error { | ||
if err := validate.V.Struct(co); err != nil { | ||
errors := err.(validator.ValidationErrors) | ||
errArr := []error{} | ||
for _, e := range errors { | ||
var errs validator.ValidationErrors | ||
errors.As(err, &errs) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
var errArr []error | ||
for _, e := range errs { | ||
// Translate each error one at a time | ||
errArr = append(errArr, fmt.Errorf(e.Translate(validate.T))) | ||
} | ||
co.configErrors = true | ||
return errArr | ||
} | ||
return nil | ||
} | ||
|
||
// AddFlags registers the common flags. | ||
func (co *ConfigOptions) AddFlags(flags *pflag.FlagSet) { | ||
flags.StringVarP(&co.configFile, "config", "c", co.configFile, "config file path (default $HOME/.driverkit.yaml if exists)") | ||
flags.VarP(co.logLevel, "loglevel", "l", "set level for logs "+co.logLevel.Allowed()) | ||
flags.IntVar(&co.Timeout, "timeout", co.Timeout, "timeout in seconds") | ||
flags.StringVar(&co.ProxyURL, "proxy", co.ProxyURL, "the proxy to use to download data") | ||
flags.BoolVar(&co.dryRun, "dryrun", co.dryRun, "do not actually perform the action") | ||
} | ||
|
||
// Init reads in config file and ENV variables if set. | ||
func (co *ConfigOptions) Init() bool { | ||
configErr := false | ||
if errs := co.validate(); errs != nil { | ||
for _, err := range errs { | ||
co.Printer.Logger.Error("error validating config options", | ||
co.Printer.Logger.Args("err", err.Error())) | ||
} | ||
configErr = true | ||
} | ||
if co.configFile != "" { | ||
viper.SetConfigFile(co.configFile) | ||
} else { | ||
// Find home directory. | ||
home, err := homedir.Dir() | ||
if err != nil { | ||
co.Printer.Logger.Error("error getting the home directory", | ||
co.Printer.Logger.Args("err", err.Error())) | ||
// not setting configErr = true because we fallback to `$HOME/.driverkit.yaml` and try with it | ||
} | ||
|
||
viper.AddConfigPath(home) | ||
viper.SetConfigName(".driverkit") | ||
} | ||
|
||
viper.AutomaticEnv() | ||
viper.SetEnvPrefix("driverkit") | ||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) | ||
|
||
// If a config file is found, read it in. | ||
err := viper.ReadInConfig() | ||
// Init printer with either read or existent one, | ||
// so that we can further log considering log level set. | ||
co.initPrinter() | ||
if err == nil { | ||
co.Printer.Logger.Info("using config file", | ||
co.Printer.Logger.Args("file", viper.ConfigFileUsed())) | ||
} else { | ||
var configFileNotFoundError viper.ConfigFileNotFoundError | ||
if errors.As(err, &configFileNotFoundError) { | ||
// Config file not found, ignore ... | ||
co.Printer.Logger.Debug("running without a configuration file") | ||
} | ||
} | ||
return configErr | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,32 +15,42 @@ limitations under the License. | |
package cmd | ||
|
||
import ( | ||
"log/slog" | ||
"os" | ||
|
||
"bytes" | ||
"github.com/falcosecurity/driverkit/pkg/driverbuilder" | ||
"github.com/falcosecurity/driverkit/pkg/driverbuilder/builder" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/pflag" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
// NewDockerCmd creates the `driverkit docker` command. | ||
func NewDockerCmd(rootOpts *RootOptions, rootFlags *pflag.FlagSet) *cobra.Command { | ||
func NewDockerCmd(configOpts *ConfigOptions, rootOpts *RootOptions, rootFlags *pflag.FlagSet) *cobra.Command { | ||
dockerCmd := &cobra.Command{ | ||
Use: "docker", | ||
Short: "Build Falco kernel modules and eBPF probes against a docker daemon.", | ||
Run: func(c *cobra.Command, args []string) { | ||
slog.With("processor", c.Name()).Info("driver building, it will take a few seconds") | ||
if !configOptions.DryRun { | ||
b := rootOpts.ToBuild() | ||
if !b.HasOutputs() { | ||
return | ||
RunE: func(c *cobra.Command, args []string) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another small change: return an error from executors instead of dealing with |
||
configOpts.Printer.Logger.Info("starting build", | ||
configOpts.Printer.Logger.Args("processor", c.Name())) | ||
if !configOpts.dryRun { | ||
if !rootOpts.Output.HasOutputs() { | ||
configOpts.Printer.Logger.Info("no output specified") | ||
return nil | ||
} | ||
if err := driverbuilder.NewDockerBuildProcessor(viper.GetInt("timeout"), viper.GetString("proxy")).Start(b); err != nil { | ||
slog.With("err", err.Error()).Error("exiting") | ||
os.Exit(1) | ||
// Since we use a spinner, cache log data to a bytesbuffer; | ||
// we will later print it once we stop the spinner. | ||
var b *builder.Build | ||
if configOpts.disableStyling { | ||
b = rootOpts.ToBuild(configOpts.Printer) | ||
} else { | ||
var buf bytes.Buffer | ||
b = rootOpts.ToBuild(configOpts.Printer.WithWriter(&buf)) | ||
configOpts.Printer.Spinner, _ = configOpts.Printer.Spinner.Start("driver building, it will take a few seconds") | ||
defer func() { | ||
configOpts.Printer.DefaultText.Print(buf.String()) | ||
}() | ||
} | ||
return driverbuilder.NewDockerBuildProcessor(configOpts.Timeout, configOpts.ProxyURL).Start(b) | ||
} | ||
return nil | ||
}, | ||
} | ||
// Add root flags | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This avoids using a deprecated method.