Skip to content

Commit

Permalink
feat: support -- arguments for sbom and other extensions [HEAD-861] (#…
Browse files Browse the repository at this point in the history
…4892)

* chore: introduce support for unknown args (—)

* chore: add test

* chore: add acceptance test

* chore: update gaf and use new constant

* chore: fix ambiguous naming

* chore: don’t attach — args when already available
  • Loading branch information
PeterSchafer authored Oct 5, 2023
1 parent 79e9dbd commit 3a9dadd
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 29 deletions.
66 changes: 44 additions & 22 deletions cliv2/cmd/cliv2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/snyk/go-application-framework/pkg/configuration"
localworkflows "github.com/snyk/go-application-framework/pkg/local_workflows"
"github.com/snyk/go-application-framework/pkg/networking"
"github.com/snyk/go-application-framework/pkg/utils"
"github.com/snyk/go-application-framework/pkg/workflow"
"github.com/snyk/go-httpauth/pkg/httpauth"
"github.com/snyk/snyk-iac-capture/pkg/capture"
Expand All @@ -43,7 +44,7 @@ import (

var internalOS string
var engine workflow.Engine
var config configuration.Configuration
var globalConfiguration configuration.Configuration
var helpProvided bool
var debugLogger = zerolog.New(zerolog.ConsoleWriter{
Out: os.Stderr,
Expand Down Expand Up @@ -141,23 +142,44 @@ func getFullCommandString(cmd *cobra.Command) string {
return name
}

func updateConfigFromParameter(config configuration.Configuration, args []string, rawArgs []string) {
// extract everything behind --
doubleDashArgs := []string{}
doubleDashPosition := -1
for i, v := range rawArgs {
if doubleDashPosition >= 0 {
doubleDashArgs = append(doubleDashArgs, v)
} else if v == "--" {
doubleDashPosition = i
}
}
config.Set(configuration.UNKNOWN_ARGS, doubleDashArgs)

// only consider the first positional argument as input directory if it is not behind a double dash.
if len(args) > 0 && !utils.Contains(doubleDashArgs, args[0]) {
config.Set(configuration.INPUT_DIRECTORY, args[0])
}
}

// main workflow
func runCommand(cmd *cobra.Command, args []string) error {
return runMainWorkflow(globalConfiguration, cmd, args, os.Args)
}

func runMainWorkflow(config configuration.Configuration, cmd *cobra.Command, args []string, rawArgs []string) error {

err := config.AddFlagSet(cmd.Flags())
if err != nil {
debugLogger.Print("Failed to add flags", err)
return err
}

updateConfigFromParameter(config, args, rawArgs)

name := getFullCommandString(cmd)
debugLogger.Print("Running ", name)
engine.GetAnalytics().SetCommand(name)

if len(args) > 0 {
config.Set(configuration.INPUT_DIRECTORY, args[0])
}

data, err := engine.Invoke(workflow.NewWorkflowIdentifier(name))
if err == nil {
_, err = engine.InvokeWithInput(localworkflows.WORKFLOWID_OUTPUT_WORKFLOW, data)
Expand Down Expand Up @@ -197,15 +219,15 @@ func defaultCmd(args []string) error {
// prepare the invocation of the legacy CLI by
// * enabling stdio
// * by specifying the raw cmd args for it
config.Set(configuration.WORKFLOW_USE_STDIO, true)
config.Set(configuration.RAW_CMD_ARGS, args)
globalConfiguration.Set(configuration.WORKFLOW_USE_STDIO, true)
globalConfiguration.Set(configuration.RAW_CMD_ARGS, args)
_, err := engine.Invoke(basic_workflows.WORKFLOWID_LEGACY_CLI)
return err
}

func getGlobalFLags() *pflag.FlagSet {
globalConfiguration := workflow.GetGlobalConfiguration()
globalFLags := workflow.FlagsetFromConfigurationOptions(globalConfiguration)
globalConfigurationOptions := workflow.GetGlobalConfiguration()
globalFLags := workflow.FlagsetFromConfigurationOptions(globalConfigurationOptions)
globalFLags.Bool(basic_workflows.PROXY_NOAUTH, false, "")
globalFLags.Bool(disable_analytics_flag, false, "")
return globalFLags
Expand Down Expand Up @@ -319,11 +341,11 @@ func handleError(err error) HandleError {
func displayError(err error) {
if err != nil {
if _, ok := err.(*exec.ExitError); !ok {
if config.GetBool(localworkflows.OUTPUT_CONFIG_KEY_JSON) {
if globalConfiguration.GetBool(localworkflows.OUTPUT_CONFIG_KEY_JSON) {
jsonError := JsonErrorStruct{
Ok: false,
ErrorMsg: err.Error(),
Path: config.GetString(configuration.INPUT_DIRECTORY),
Path: globalConfiguration.GetString(configuration.INPUT_DIRECTORY),
}

jsonErrorBuffer, _ := json.MarshalIndent(jsonError, "", " ")
Expand Down Expand Up @@ -439,20 +461,20 @@ func MainWithErrorCode() int {
_ = rootCommand.ParseFlags(os.Args)

// create engine
config = configuration.New()
err = config.AddFlagSet(rootCommand.LocalFlags())
globalConfiguration = configuration.New()
err = globalConfiguration.AddFlagSet(rootCommand.LocalFlags())
if err != nil {
debugLogger.Print("Failed to add flags to root command", err)
}

debugEnabled := config.GetBool(configuration.DEBUG)
debugLogger := getDebugLogger(config)
debugEnabled := globalConfiguration.GetBool(configuration.DEBUG)
debugLogger := getDebugLogger(globalConfiguration)

initApplicationConfiguration(config)
engine = app.CreateAppEngineWithOptions(app.WithZeroLogger(debugLogger), app.WithConfiguration(config))
initApplicationConfiguration(globalConfiguration)
engine = app.CreateAppEngineWithOptions(app.WithZeroLogger(debugLogger), app.WithConfiguration(globalConfiguration))

if noProxyAuth := config.GetBool(basic_workflows.PROXY_NOAUTH); noProxyAuth {
config.Set(configuration.PROXY_AUTHENTICATION_MECHANISM, httpauth.StringFromAuthenticationMechanism(httpauth.NoAuth))
if noProxyAuth := globalConfiguration.GetBool(basic_workflows.PROXY_NOAUTH); noProxyAuth {
globalConfiguration.Set(configuration.PROXY_AUTHENTICATION_MECHANISM, httpauth.StringFromAuthenticationMechanism(httpauth.NoAuth))
}

// initialize the extensions -> they register themselves at the engine
Expand Down Expand Up @@ -485,21 +507,21 @@ func MainWithErrorCode() int {
networkAccess.AddHeaderField(
"User-Agent",
networking.UserAgent(
networking.UaWithConfig(config),
networking.UaWithConfig(globalConfiguration),
networking.UaWithApplication("snyk-cli", cliv2.GetFullVersion()),
networking.UaWithOS(internalOS)).String(),
)

if debugEnabled {
writeLogHeader(config, networkAccess)
writeLogHeader(globalConfiguration, networkAccess)
}

// init Analytics
cliAnalytics := engine.GetAnalytics()
cliAnalytics.SetVersion(cliv2.GetFullVersion())
cliAnalytics.SetCmdArguments(os.Args[1:])
cliAnalytics.SetOperatingSystem(internalOS)
if config.GetBool(configuration.ANALYTICS_DISABLED) == false {
if globalConfiguration.GetBool(configuration.ANALYTICS_DISABLED) == false {
defer sendAnalytics(cliAnalytics, debugLogger)
}

Expand Down
76 changes: 72 additions & 4 deletions cliv2/cmd/cliv2/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import (
"testing"

"github.com/snyk/go-application-framework/pkg/configuration"
localworkflows "github.com/snyk/go-application-framework/pkg/local_workflows"
"github.com/snyk/go-application-framework/pkg/workflow"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
)

func cleanup() {
helpProvided = false
config = nil
globalConfiguration = nil
engine = nil
}

Expand Down Expand Up @@ -87,9 +89,9 @@ func Test_initApplicationConfiguration_DisablesAnalytics(t *testing.T) {
func Test_CreateCommandsForWorkflowWithSubcommands(t *testing.T) {
defer cleanup()

config = configuration.New()
config.Set(configuration.DEBUG, true)
engine = workflow.NewWorkFlowEngine(config)
globalConfiguration = configuration.New()
globalConfiguration.Set(configuration.DEBUG, true)
engine = workflow.NewWorkFlowEngine(globalConfiguration)

fn := func(invocation workflow.InvocationContext, input []workflow.Data) ([]workflow.Data, error) {
return []workflow.Data{}, nil
Expand Down Expand Up @@ -145,3 +147,69 @@ func Test_CreateCommandsForWorkflowWithSubcommands(t *testing.T) {
assert.Equal(t, "cmd2", cmd2.Name())
assert.True(t, cmd2.Hidden)
}

func Test_runMainWorkflow_unknownargs(t *testing.T) {

tests := map[string]struct {
inputDir string
unknownArgs []string
}{
"input dir with unknown arguments": {inputDir: "a/b/c", unknownArgs: []string{"a", "b", "c"}},
"no input dir with unknown arguments": {inputDir: "", unknownArgs: []string{"a", "b", "c"}},
"input dir without unknown arguments": {inputDir: "a", unknownArgs: []string{}},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {

expectedInputDir := tc.inputDir
expectedUnknownArgs := tc.unknownArgs

defer cleanup()
globalConfiguration = configuration.New()
globalConfiguration.Set(configuration.DEBUG, true)
engine = workflow.NewWorkFlowEngine(globalConfiguration)

fn := func(invocation workflow.InvocationContext, input []workflow.Data) ([]workflow.Data, error) {
return []workflow.Data{}, nil
}

// setup workflow engine to contain a workflow with subcommands
commandList := []string{"command", localworkflows.WORKFLOWID_OUTPUT_WORKFLOW.Host}
for _, v := range commandList {
workflowConfig := workflow.ConfigurationOptionsFromFlagset(pflag.NewFlagSet("pla", pflag.ContinueOnError))
workflowId1 := workflow.NewWorkflowIdentifier(v)
_, err := engine.Register(workflowId1, workflowConfig, fn)
if err != nil {
t.Fatal(err)
}
}

_ = engine.Init()

config := configuration.NewInMemory()
cmd := &cobra.Command{
Use: "command",
}

positionalArgs := []string{expectedInputDir}
positionalArgs = append(positionalArgs, expectedUnknownArgs...)

rawArgs := []string{"app", "command", "--sad", expectedInputDir}
if len(expectedUnknownArgs) > 0 {
rawArgs = append(rawArgs, "--")
rawArgs = append(rawArgs, expectedUnknownArgs...)
}

// call method under test
err := runMainWorkflow(config, cmd, positionalArgs, rawArgs)
assert.Nil(t, err)

actualInputDir := config.GetString(configuration.INPUT_DIRECTORY)
assert.Equal(t, expectedInputDir, actualInputDir)

actualUnknownArgs := config.GetStringSlice(configuration.UNKNOWN_ARGS)
assert.Equal(t, expectedUnknownArgs, actualUnknownArgs)
})
}
}
2 changes: 1 addition & 1 deletion cliv2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/snyk/cli-extension-iac-rules v0.0.0-20230601153200-c572cfce46ce
github.com/snyk/cli-extension-sbom v0.0.0-20230926124903-9705d7d47d8f
github.com/snyk/container-cli v0.0.0-20230920093251-fe865879a91f
github.com/snyk/go-application-framework v0.0.0-20230915105125-18e4f97ef870
github.com/snyk/go-application-framework v0.0.0-20231004153030-f8b8df23bcf4
github.com/snyk/go-httpauth v0.0.0-20230726132335-d454674305a7
github.com/snyk/snyk-iac-capture v0.6.0
github.com/snyk/snyk-ls v0.0.0-20230911152618-39fc0f68d431
Expand Down
Loading

0 comments on commit 3a9dadd

Please sign in to comment.