From 589ad278355579ff6b8b6c38cabb34535b87542d Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Thu, 11 Apr 2024 12:18:03 +0200 Subject: [PATCH] chore(cmd,pkg): allow cli test to disable styling. Signed-off-by: Federico Di Pierro --- cmd/cli_test.go | 12 ++++-------- cmd/completion.go | 16 +++++++--------- cmd/config_options.go | 28 +++++++++++++++++----------- cmd/docker.go | 28 ++++++++++++++++------------ cmd/images.go | 23 +++++++++++++++++------ cmd/kubernetes.go | 29 ++++++++++++++++------------- cmd/kubernetes_in_cluster.go | 29 ++++++++++++++++------------- cmd/local.go | 28 ++++++++++++++++------------ cmd/root.go | 12 ++++++++++-- cmd/root_options.go | 4 ++++ pkg/driverbuilder/builder/build.go | 4 ---- 11 files changed, 123 insertions(+), 90 deletions(-) diff --git a/cmd/cli_test.go b/cmd/cli_test.go index 9b734c98..7064a200 100644 --- a/cmd/cli_test.go +++ b/cmd/cli_test.go @@ -19,7 +19,6 @@ package cmd import ( "bytes" - "io" "io/ioutil" "os" "path/filepath" @@ -319,9 +318,9 @@ func run(t *testing.T, test testCase) { assert.NilError(t, err) rootOpts, err := NewRootOptions() assert.NilError(t, err) + var buf bytes.Buffer + configOpts.setOutput(&buf, true) c := NewRootCmd(configOpts, rootOpts) - b := bytes.NewBufferString("") - configOpts.SetOutput(b) if len(test.args) == 0 || (test.args[0] != "__complete" && test.args[0] != "__completeNoDesc" && test.args[0] != "help" && test.args[0] != "completion") { test.args = append(test.args, "--dryrun") } @@ -340,11 +339,8 @@ func run(t *testing.T, test testCase) { assert.Error(t, err, test.expect.err) } } - out, err := io.ReadAll(b) - if err != nil { - t.Fatalf("error reading CLI output: %v", err) - } - res := stripansi.Strip(string(out)) + out := buf.String() + res := stripansi.Strip(out) assert.Equal(t, test.expect.out, res) // Teardown for k := range test.env { diff --git a/cmd/completion.go b/cmd/completion.go index b1199cad..1675e337 100644 --- a/cmd/completion.go +++ b/cmd/completion.go @@ -66,25 +66,23 @@ func NewCompletionCmd(_ *ConfigOptions, _ *RootOptions, _ *pflag.FlagSet) *cobra 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 }, } diff --git a/cmd/config_options.go b/cmd/config_options.go index 6173c2d6..5a2237f1 100644 --- a/cmd/config_options.go +++ b/cmd/config_options.go @@ -38,32 +38,38 @@ var aliasProcessors = []string{"docker", "k8s", "k8s-ic"} // ConfigOptions represent the persistent configuration flags of driverkit. type ConfigOptions struct { configFile string - timeout int `validate:"number,min=30" default:"120" name:"timeout"` - proxyURL string `validate:"omitempty,proxy" name:"proxy url"` + Timeout int `validate:"number,min=30" default:"120" name:"timeout"` + ProxyURL string `validate:"omitempty,proxy" name:"proxy url"` dryRun bool // Printer used by all commands to output messages. Printer *output.Printer // writer is used to write the output of the printer. - writer io.Writer - logLevel *options.LogLevel + writer io.Writer + logLevel *options.LogLevel + disableStyling bool } func (co *ConfigOptions) initPrinter() { - logLevel := co.logLevel.ToPtermLogLevel() - co.Printer = output.NewPrinter(logLevel, pterm.LogFormatterColorful, co.writer) + if co.disableStyling { + pterm.DisableColor() + } + co.Printer = output.NewPrinter(co.logLevel.ToPtermLogLevel(), pterm.LogFormatterColorful, co.writer) } -func (co *ConfigOptions) SetOutput(writer io.Writer) { +// 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, error) { o := &ConfigOptions{ - writer: os.Stdout, - logLevel: options.NewLogLevel(), + writer: os.Stdout, + logLevel: options.NewLogLevel(), + disableStyling: false, } o.initPrinter() if err := defaults.Set(o); err != nil { @@ -92,8 +98,8 @@ func (co *ConfigOptions) validate() []error { 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.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") } diff --git a/cmd/docker.go b/cmd/docker.go index 9a50cd20..a99e9f6b 100644 --- a/cmd/docker.go +++ b/cmd/docker.go @@ -17,6 +17,7 @@ package cmd import ( "bytes" "github.com/falcosecurity/driverkit/pkg/driverbuilder" + "github.com/falcosecurity/driverkit/pkg/driverbuilder/builder" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -30,21 +31,24 @@ func NewDockerCmd(configOpts *ConfigOptions, rootOpts *RootOptions, rootFlags *p 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 + } // Since we use a spinner, cache log data to a bytesbuffer; // we will later print it once we stop the spinner. - var buf bytes.Buffer - b := rootOpts.ToBuild(configOpts.Printer.WithWriter(&buf)) - defer func() { - configOpts.Printer.DefaultText.Print(buf.String()) - }() - if !b.HasOutputs() { - return nil + 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()) + }() } - configOpts.Printer.Spinner, _ = configOpts.Printer.Spinner.Start("driver building, it will take a few seconds") - defer func() { - _ = configOpts.Printer.Spinner.Stop() - }() - return driverbuilder.NewDockerBuildProcessor(configOpts.timeout, configOpts.proxyURL).Start(b) + return driverbuilder.NewDockerBuildProcessor(configOpts.Timeout, configOpts.ProxyURL).Start(b) } return nil }, diff --git a/cmd/images.go b/cmd/images.go index e8507fe0..4fcdb823 100644 --- a/cmd/images.go +++ b/cmd/images.go @@ -16,6 +16,7 @@ package cmd import ( "bytes" + "github.com/falcosecurity/driverkit/pkg/driverbuilder/builder" "os" "github.com/olekukonko/tablewriter" @@ -28,17 +29,26 @@ func NewImagesCmd(configOpts *ConfigOptions, rootOpts *RootOptions, rootFlags *p imagesCmd := &cobra.Command{ Use: "images", Short: "List builder images", - Run: func(c *cobra.Command, args []string) { + RunE: func(c *cobra.Command, args []string) error { configOpts.Printer.Logger.Info("starting loading images", configOpts.Printer.Logger.Args("processor", c.Name())) // Since we use a spinner, cache log data to a bytesbuffer; // we will later print it once we stop the spinner. - var buf bytes.Buffer - b := rootOpts.ToBuild(configOpts.Printer.WithWriter(&buf)) - configOpts.Printer.Spinner, _ = configOpts.Printer.Spinner.Start("listing images, it will take a few seconds") + var ( + buf bytes.Buffer + b *builder.Build + ) + if configOpts.disableStyling { + b = rootOpts.ToBuild(configOpts.Printer) + } else { + b = rootOpts.ToBuild(configOpts.Printer.WithWriter(&buf)) + configOpts.Printer.Spinner, _ = configOpts.Printer.Spinner.Start("listing images, it will take a few seconds") + } b.LoadImages() - _ = configOpts.Printer.Spinner.Stop() - configOpts.Printer.DefaultText.Print(buf.String()) + if !configOpts.disableStyling { + _ = configOpts.Printer.Spinner.Stop() + configOpts.Printer.DefaultText.Print(buf.String()) + } table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"Image", "Target", "Arch", "GCC"}) @@ -54,6 +64,7 @@ func NewImagesCmd(configOpts *ConfigOptions, rootOpts *RootOptions, rootFlags *p table.Append(data) } table.Render() // Send output + return nil }, } // Add root flags diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index 69bf2295..0859543f 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -61,20 +61,23 @@ func NewKubernetesCmd(configOpts *ConfigOptions, rootOpts *RootOptions, rootFlag 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 + } // Since we use a spinner, cache log data to a bytesbuffer; // we will later print it once we stop the spinner. - var buf bytes.Buffer - b := rootOpts.ToBuild(configOpts.Printer.WithWriter(&buf)) - defer func() { - configOpts.Printer.DefaultText.Print(buf.String()) - }() - if !b.HasOutputs() { - return nil + 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()) + }() } - configOpts.Printer.Spinner, _ = configOpts.Printer.Spinner.Start("driver building, it will take a few seconds") - defer func() { - _ = configOpts.Printer.Spinner.Stop() - }() return kubernetesRun(kubefactory, b, configOpts) } return nil @@ -104,7 +107,7 @@ func kubernetesRun(kubefactory factory.Factory, kubernetesOptions.RunAsUser, kubernetesOptions.Namespace, kubernetesOptions.ImagePullSecret, - configOpts.timeout, - configOpts.proxyURL) + configOpts.Timeout, + configOpts.ProxyURL) return buildProcessor.Start(b) } diff --git a/cmd/kubernetes_in_cluster.go b/cmd/kubernetes_in_cluster.go index 310dc677..31a1e81f 100644 --- a/cmd/kubernetes_in_cluster.go +++ b/cmd/kubernetes_in_cluster.go @@ -44,20 +44,23 @@ func NewKubernetesInClusterCmd(configOpts *ConfigOptions, rootOpts *RootOptions, 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 + } // Since we use a spinner, cache log data to a bytesbuffer; // we will later print it once we stop the spinner. - var buf bytes.Buffer - b := rootOpts.ToBuild(configOpts.Printer.WithWriter(&buf)) - defer func() { - configOpts.Printer.DefaultText.Print(buf.String()) - }() - if !b.HasOutputs() { - return nil + 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()) + }() } - configOpts.Printer.Spinner, _ = configOpts.Printer.Spinner.Start("driver building, it will take a few seconds") - defer func() { - _ = configOpts.Printer.Spinner.Stop() - }() return kubernetesInClusterRun(b, configOpts) } return nil @@ -85,7 +88,7 @@ func kubernetesInClusterRun(b *builder.Build, configOpts *ConfigOptions) error { kubernetesOptions.RunAsUser, kubernetesOptions.Namespace, kubernetesOptions.ImagePullSecret, - configOpts.timeout, - configOpts.proxyURL) + configOpts.Timeout, + configOpts.ProxyURL) return buildProcessor.Start(b) } diff --git a/cmd/local.go b/cmd/local.go index b16fbb72..01c850d6 100644 --- a/cmd/local.go +++ b/cmd/local.go @@ -3,6 +3,7 @@ package cmd import ( "bytes" "github.com/falcosecurity/driverkit/pkg/driverbuilder" + "github.com/falcosecurity/driverkit/pkg/driverbuilder/builder" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -24,25 +25,28 @@ func NewLocalCmd(configOpts *ConfigOptions, rootOpts *RootOptions, rootFlags *pf 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 + } // Since we use a spinner, cache log data to a bytesbuffer; // we will later print it once we stop the spinner. - var buf bytes.Buffer - b := rootOpts.ToBuild(configOpts.Printer.WithWriter(&buf)) - defer func() { - configOpts.Printer.DefaultText.Print(buf.String()) - }() - if !b.HasOutputs() { - return nil + 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()) + }() } - configOpts.Printer.Spinner, _ = configOpts.Printer.Spinner.Start("driver building, it will take a few seconds") - defer func() { - _ = configOpts.Printer.Spinner.Stop() - }() return driverbuilder.NewLocalBuildProcessor(opts.useDKMS, opts.downloadHeaders, opts.srcDir, opts.envMap, - configOpts.timeout).Start(b) + configOpts.Timeout).Start(b) } return nil }, diff --git a/cmd/root.go b/cmd/root.go index 3e52d10d..717f9c10 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -112,13 +112,14 @@ func NewRootCmd(configOpts *ConfigOptions, rootOpts *RootOptions) *RootCmd { Args: cobra.OnlyValidArgs, DisableFlagsInUseLine: true, DisableAutoGenTag: true, + SilenceUsage: true, Version: version.String(), - Run: func(c *cobra.Command, args []string) { + RunE: func(c *cobra.Command, args []string) error { if len(args) == 0 { configOpts.Printer.Logger.Info("specify a valid processor", configOpts.Printer.Logger.Args("processors", validProcessors)) } // Fallback to help - _ = c.Help() + return c.Help() }, } ret := &RootCmd{ @@ -210,6 +211,13 @@ func Start() { configOpts.Printer.Logger.Fatal("error setting driverkit root options defaults", configOpts.Printer.Logger.Args("err", err.Error())) } + + // Cleanup spinner upon leaving if any + defer func() { + if configOpts.Printer.Spinner != nil { + _ = configOpts.Printer.Spinner.Stop() + } + }() root := NewRootCmd(configOpts, rootOpts) if err = root.Execute(); err != nil { configOpts.Printer.Logger.Fatal("error executing driverkit", configOpts.Printer.Logger.Args("err", err.Error())) diff --git a/cmd/root_options.go b/cmd/root_options.go index efb064a7..e8fdae7e 100644 --- a/cmd/root_options.go +++ b/cmd/root_options.go @@ -36,6 +36,10 @@ type OutputOptions struct { Probe string `validate:"required_without=Module,filepath,omitempty,endswith=.o" name:"output probe path"` } +func (oo *OutputOptions) HasOutputs() bool { + return oo.Module != "" || oo.Probe != "" +} + type RepoOptions struct { Org string `default:"falcosecurity" name:"organization name"` Name string `default:"libs" name:"repo name"` diff --git a/pkg/driverbuilder/builder/build.go b/pkg/driverbuilder/builder/build.go index 6d0e8558..650b0a48 100644 --- a/pkg/driverbuilder/builder/build.go +++ b/pkg/driverbuilder/builder/build.go @@ -111,7 +111,3 @@ func (b *Build) ClientForRegistry(registry string) *auth.Client { return client } - -func (b *Build) HasOutputs() bool { - return b.ModuleFilePath != "" || b.ProbeFilePath != "" -}