From 8204f095f6527143d115fc424b4b542f8f12c8c7 Mon Sep 17 00:00:00 2001 From: Salim Afiune Date: Thu, 10 Nov 2022 16:10:48 -0800 Subject: [PATCH] feat(cdk): improve honeyvent data for components (#1016) Improves how we send a Honeyvent for components, this change also moves the logic of parsing flags and initializing CLI state to the PersistentPreRunE() func. * ci: debug issue opening PR during release * chore(cli): add missing copyright headers * fix: pass parser error to honeyvent Signed-off-by: Salim Afiune Maya --- cli/cmd/cli_state.go | 29 ++++++----- cli/cmd/component.go | 26 ++-------- cli/cmd/component_args.go | 17 +++++++ cli/cmd/component_args_test.go | 17 +++++++ cli/cmd/honeyvent.go | 1 + cli/cmd/root.go | 92 +++++++++++++++++++++------------- scripts/release.sh | 3 ++ 7 files changed, 116 insertions(+), 69 deletions(-) diff --git a/cli/cmd/cli_state.go b/cli/cmd/cli_state.go index c67ee1e51..588af00e8 100644 --- a/cli/cmd/cli_state.go +++ b/cli/cmd/cli_state.go @@ -63,18 +63,19 @@ type cliState struct { Cache *diskv.Diskv LwComponents *lwcomponent.State - id string - workers sync.WaitGroup - spinner *spinner.Spinner - jsonOutput bool - yamlOutput bool - csvOutput bool - nonInteractive bool - noCache bool - lqlOperator string - profileDetails map[string]interface{} - tokenCache api.TokenData - installedCmd bool + id string + workers sync.WaitGroup + spinner *spinner.Spinner + jsonOutput bool + yamlOutput bool + csvOutput bool + nonInteractive bool + noCache bool + lqlOperator string + profileDetails map[string]interface{} + tokenCache api.TokenData + installedCmd bool + componentParser componentArgParser } // NewDefaultState creates a new cliState with some defaults @@ -430,6 +431,10 @@ func (c *cliState) envs() []string { fmt.Sprintf("LW_NOCOLOR=%s", os.Getenv("NO_COLOR")), fmt.Sprintf("LW_LOG=%s", c.LogLevel), fmt.Sprintf("LW_JSON=%v", c.jsonOutput), + + // Sends tracing information + fmt.Sprintf("LW_HONEYVENT_TRACE_ID=%s", c.Event.TraceID), + fmt.Sprintf("LW_HONEYVENT_PARENT_ID=%s", c.Event.SpanID), } } diff --git a/cli/cmd/component.go b/cli/cmd/component.go index a75ac498d..f4259a581 100644 --- a/cli/cmd/component.go +++ b/cli/cmd/component.go @@ -164,35 +164,15 @@ func (c *cliState) LoadComponents() { DisableFlagParsing: true, DisableFlagsInUseLine: true, RunE: func(cmd *cobra.Command, args []string) error { - - // Split args into those handled by the CLI and those - // we pass to the component - argParser := componentArgParser{} - argParser.parseArgs(cmd.Flags(), args) - err := cmd.Flags().Parse(argParser.cliArgs) - - // We call initConfig() again after global flags have been parsed. - initConfig() - - if err != nil { - cli.Log.Debugw("unable to parse global flags", - "provided_flags", argParser.cliArgs, "error", err) - } - - // The root command's persistent pre-run will not have created - // a client or done migrations - if err := rootPersistentPreRunE(); err != nil { - return err - } - cli.Log.Debugw("running component", "component", cmd.Use, - "args", argParser.componentArgs, "cli_flags", argParser.cliArgs) + "args", cli.componentParser.componentArgs, + "cli_flags", cli.componentParser.cliArgs) f, ok := cli.LwComponents.GetComponent(cmd.Use) if ok { // @afiune what if the component needs other env variables envs := []string{fmt.Sprintf("LW_COMPONENT_NAME=%s", cmd.Use)} envs = append(envs, c.envs()...) - return f.RunAndOutput(argParser.componentArgs, envs...) + return f.RunAndOutput(cli.componentParser.componentArgs, envs...) } // We will land here only if we couldn't run the component, which is not diff --git a/cli/cmd/component_args.go b/cli/cmd/component_args.go index 35b7c1cc3..a951424fa 100644 --- a/cli/cmd/component_args.go +++ b/cli/cmd/component_args.go @@ -1,3 +1,20 @@ +// +// Copyright:: Copyright 2022, Lacework Inc. +// License:: Apache License, Version 2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + package cmd import ( diff --git a/cli/cmd/component_args_test.go b/cli/cmd/component_args_test.go index eabe438c4..fab6420aa 100644 --- a/cli/cmd/component_args_test.go +++ b/cli/cmd/component_args_test.go @@ -1,3 +1,20 @@ +// +// Copyright:: Copyright 2022, Lacework Inc. +// License:: Apache License, Version 2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + package cmd import ( diff --git a/cli/cmd/honeyvent.go b/cli/cmd/honeyvent.go index 5c49089d3..9de431d37 100644 --- a/cli/cmd/honeyvent.go +++ b/cli/cmd/honeyvent.go @@ -109,6 +109,7 @@ type Honeyvent struct { DurationMs int64 `json:"duration_ms,omitempty"` Error string `json:"error,omitempty"` InstallMethod string `json:"install_method,omitempty"` + Component string `json:"component,omitempty"` // tracing data for multiple events, this is useful for specific features // within the Lacework CLI such as daily version check, polling mechanism, etc. diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 6fdf47021..5c90c702d 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -33,19 +33,6 @@ import ( "github.com/lacework/go-sdk/lwlogger" ) -func rootPersistentPreRunE() error { - if err := cli.NewClient(); err != nil { - if !strings.Contains(err.Error(), "Invalid Account") { - return err - } - - if err := cli.Migrations(); err != nil { - return err - } - } - return nil -} - var ( // the global cli state with defaults cli = NewDefaultState() @@ -66,27 +53,10 @@ Start by configuring the Lacework CLI with the command: This will prompt you for your Lacework account and a set of API access keys.`, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - cli.Log.Debugw("updating honeyvent", "dataset", HoneyDataset) - cli.Event.Command = cmd.CommandPath() - cli.Event.Args = args - cli.Event.Flags = parseFlags(os.Args[1:]) - cli.SendHoneyvent() - - switch cmd.Use { - case "help [command]", "configure", "version", "docs ", "generate-pkg-manifest": - return nil - default: - // @afiune no need to create a client for any configure command - if cmd.HasParent() && cmd.Parent().Use == "configure" { - return nil - } - // @dhazekamp no need to create a client for any component command - // this will be handled via the components RunE - if isComponent(cmd.Annotations) { - return nil - } - return rootPersistentPreRunE() + if isComponent(cmd.Annotations) { + return componentPersistentPreRun(cmd, args) } + return cliPersistentPreRun(cmd, args) }, PersistentPostRunE: func(cmd *cobra.Command, _ []string) error { // skip daily version check if the user is running the version command @@ -106,6 +76,61 @@ This will prompt you for your Lacework account and a set of API access keys.`, } ) +func cliPersistentPreRun(cmd *cobra.Command, args []string) error { + cli.Log.Debugw("updating honeyvent", "dataset", HoneyDataset) + cli.Event.Command = cmd.CommandPath() + cli.Event.Args = args + cli.Event.Flags = parseFlags(os.Args[1:]) + cli.SendHoneyvent() + + switch cmd.Use { + case "help [command]", "configure", "version", "docs ", "generate-pkg-manifest": + return nil + default: + // @afiune no need to create a client for any configure command + if cmd.HasParent() && cmd.Parent().Use == "configure" { + return nil + } + if err := cli.NewClient(); err != nil { + if !strings.Contains(err.Error(), "Invalid Account") { + return err + } + + if err := cli.Migrations(); err != nil { + return err + } + } + } + return nil +} + +func componentPersistentPreRun(cmd *cobra.Command, args []string) error { + cli.Event.Command = cmd.CommandPath() + cli.Event.Component = cmd.Use + cli.Event.Flags = parseFlags(os.Args[1:]) + defer cli.SendHoneyvent() + + // For components, we disable flag parsing, therefore we + // split args into those handled by the CLI and those + // we pass to the component manually + cli.componentParser.parseArgs(cmd.Flags(), args) + cli.Event.Args = cli.componentParser.componentArgs + err := cmd.Flags().Parse(cli.componentParser.cliArgs) + + // We call initConfig() again after global flags have been parsed. + initConfig() + + if err != nil { + cli.Event.Error = err.Error() + cli.Log.Debugw("unable to parse global flags", "error", err, + "provided_flags", cli.componentParser.cliArgs) + } + + cli.Log.Debugw("honeyvent updated", "dataset", HoneyDataset) + + return cli.NewClient() +} + // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() (err error) { @@ -280,7 +305,6 @@ func initConfig() { cli.Log.Debugw("configuration file not found") } else { // the config file was found but another error was produced - errcheckWARN(rootCmd.Help()) exitwith(errors.Wrap(err, "unable to read in config file ~/.lacework.toml")) } } else { diff --git a/scripts/release.sh b/scripts/release.sh index bf9e21472..a8fee8e94 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -297,6 +297,9 @@ open_pull_request() { curl -XPOST -H "Authorization: token $GITHUB_TOKEN" --data "@$_body" \ https://api.github.com/repos/${org_name}/${project_name}/pulls > $_pr + # @afiune just to debug the issue where the field `html_url` comes as `null` + echo "$_pr" | jq . + _pr_url=$(jq .html_url $_pr) log "" log "It is time to review the release!"