Skip to content
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

feat: parse workflow data to determine errors #5157

Merged
merged 22 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2c5e5ff
feat: parse workflow data to determine errors
thisislawatts Apr 10, 2024
314dca4
fix: switch to align with finalised schema
thisislawatts Apr 10, 2024
e2745cf
chore(deps): bump gaf to latest
thisislawatts Apr 12, 2024
218ec7e
test: refactor to support integration test
thisislawatts Apr 12, 2024
dcc98b6
fix: introduce custom error for storing exit code
thisislawatts Apr 12, 2024
b46edad
chore: adjust wording on json error
thisislawatts Apr 12, 2024
c3bd5ba
test: update to match new error
thisislawatts Apr 12, 2024
75406b7
chore: remove file
thisislawatts Apr 12, 2024
68e5a9e
chore(deps): update go-application-framework to latest
thisislawatts Apr 12, 2024
a901477
chore: reorder imports
thisislawatts Apr 12, 2024
2cf291e
chore: remove unused code
thisislawatts Apr 12, 2024
b18ee77
refactor: switch to structured test data
thisislawatts Apr 12, 2024
26646c7
chore: fix formatting
thisislawatts Apr 12, 2024
1320d7c
chore: rename to include global prefix
thisislawatts Apr 12, 2024
430849d
fix: switch to content_type ref
thisislawatts Apr 12, 2024
fbe818a
chore: remove unused file
thisislawatts Apr 15, 2024
51c57d9
refactor: switch to exported type
thisislawatts Apr 15, 2024
9a9c66d
refactor: introduce tests for displayError
thisislawatts Apr 15, 2024
e09cb58
test: switch to NewInMemory configuration
thisislawatts Apr 15, 2024
340cdac
fix: display error logic to handle ExitCode errors
PeterSchafer Apr 15, 2024
f85ca30
fix: broken import
PeterSchafer Apr 15, 2024
9b131a8
test: remove defunct test
thisislawatts Apr 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 83 additions & 35 deletions cliv2/cmd/cliv2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"github.com/snyk/go-application-framework/pkg/auth"
"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/local_workflows/content_type"
"github.com/snyk/go-application-framework/pkg/local_workflows/json_schemas"
"github.com/snyk/go-application-framework/pkg/networking"
"github.com/snyk/go-application-framework/pkg/runtimeinfo"
"github.com/snyk/go-application-framework/pkg/utils"
Expand All @@ -37,11 +39,12 @@ import (

"github.com/snyk/cli/cliv2/internal/cliv2"
"github.com/snyk/cli/cliv2/internal/constants"
cli_errors "github.com/snyk/cli/cliv2/internal/errors"
"github.com/snyk/cli/cliv2/pkg/basic_workflows"
)

var internalOS string
var engine workflow.Engine
var globalEngine workflow.Engine
var globalConfiguration configuration.Configuration
var helpProvided bool

Expand Down Expand Up @@ -154,18 +157,59 @@ func runMainWorkflow(config configuration.Configuration, cmd *cobra.Command, arg

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

err = runWorkflowAndProcessData(globalEngine, globalLogger, name)

return err
}

func runWorkflowAndProcessData(engine workflow.Engine, logger *zerolog.Logger, name string) error {
data, err := engine.Invoke(workflow.NewWorkflowIdentifier(name))

if err == nil {
_, err = engine.InvokeWithInput(localworkflows.WORKFLOWID_OUTPUT_WORKFLOW, data)
if err == nil {
err = getErrorFromWorkFlowData(data)
}
} else {
globalLogger.Print("Failed to execute the command!", err)
logger.Print("Failed to execute the command!", err)
}

return err
}

func getErrorFromWorkFlowData(data []workflow.Data) error {
for i := range data {
mimeType := data[i].GetContentType()
if strings.EqualFold(mimeType, content_type.TEST_SUMMARY) {
singleData, ok := data[i].GetPayload().([]byte)
if !ok {
return fmt.Errorf("invalid payload type: %T", data[i].GetPayload())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: For error messages to be a bit more self explanatory maybe mention what failed and then the why.

}

summary := json_schemas.TestSummary{}

err := json.Unmarshal(singleData, &summary)
if err != nil {
return fmt.Errorf("failed to parse test summary payload: %w", err)
}

// We are missing an understanding of ignored issues here
// this should be supported in the future
for _, result := range summary.Results {
if result.Open > 0 {
return cli_errors.ErrorWithExitCode{
ExitCode: constants.SNYK_EXIT_CODE_VULNERABILITIES_FOUND,
}
}
}

return nil
}
}
return nil
}

func sendAnalytics(analytics analytics.Analytics, debugLogger *zerolog.Logger) {
debugLogger.Print("Sending Analytics")

Expand Down Expand Up @@ -202,7 +246,7 @@ func defaultCmd(args []string) error {
// * by specifying the raw cmd args for it
globalConfiguration.Set(configuration.WORKFLOW_USE_STDIO, true)
globalConfiguration.Set(configuration.RAW_CMD_ARGS, args)
_, err := engine.Invoke(basic_workflows.WORKFLOWID_LEGACY_CLI)
_, err := globalEngine.Invoke(basic_workflows.WORKFLOWID_LEGACY_CLI)
return err
}

Expand Down Expand Up @@ -324,25 +368,29 @@ func handleError(err error) HandleError {
return resultError
}

func displayError(err error) {
func displayError(err error, output io.Writer, config configuration.Configuration) {
if err != nil {
var exitError *exec.ExitError
if !errors.As(err, &exitError) {
if globalConfiguration.GetBool(localworkflows.OUTPUT_CONFIG_KEY_JSON) {
jsonError := JsonErrorStruct{
Ok: false,
ErrorMsg: err.Error(),
Path: globalConfiguration.GetString(configuration.INPUT_DIRECTORY),
}
isExitError := errors.As(err, &exitError)
isErrorWithCode := errors.As(err, &cli_errors.ErrorWithExitCode{})
if isExitError || isErrorWithCode {
return
}

jsonErrorBuffer, _ := json.MarshalIndent(jsonError, "", " ")
fmt.Println(string(jsonErrorBuffer))
if config.GetBool(localworkflows.OUTPUT_CONFIG_KEY_JSON) {
jsonError := JsonErrorStruct{
Ok: false,
ErrorMsg: err.Error(),
Path: globalConfiguration.GetString(configuration.INPUT_DIRECTORY),
}

jsonErrorBuffer, _ := json.MarshalIndent(jsonError, "", " ")
fmt.Fprintln(output, string(jsonErrorBuffer))
} else {
if errors.Is(err, context.DeadlineExceeded) {
fmt.Fprintln(output, "command timed out")
} else {
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("command timed out")
} else {
fmt.Println(err)
}
fmt.Fprintln(output, err)
}
}
}
Expand All @@ -368,39 +416,39 @@ func MainWithErrorCode() int {
debugEnabled := globalConfiguration.GetBool(configuration.DEBUG)
globalLogger = initDebugLogger(globalConfiguration)

engine = app.CreateAppEngineWithOptions(app.WithZeroLogger(globalLogger), app.WithConfiguration(globalConfiguration), app.WithRuntimeInfo(rInfo))
globalEngine = app.CreateAppEngineWithOptions(app.WithZeroLogger(globalLogger), app.WithConfiguration(globalConfiguration), app.WithRuntimeInfo(rInfo))

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
engine.AddExtensionInitializer(basic_workflows.Init)
engine.AddExtensionInitializer(sbom.Init)
engine.AddExtensionInitializer(depgraph.Init)
engine.AddExtensionInitializer(capture.Init)
engine.AddExtensionInitializer(iacrules.Init)
engine.AddExtensionInitializer(snykls.Init)
engine.AddExtensionInitializer(container.Init)
engine.AddExtensionInitializer(localworkflows.InitCodeWorkflow)
globalEngine.AddExtensionInitializer(basic_workflows.Init)
globalEngine.AddExtensionInitializer(sbom.Init)
globalEngine.AddExtensionInitializer(depgraph.Init)
globalEngine.AddExtensionInitializer(capture.Init)
globalEngine.AddExtensionInitializer(iacrules.Init)
globalEngine.AddExtensionInitializer(snykls.Init)
globalEngine.AddExtensionInitializer(container.Init)
globalEngine.AddExtensionInitializer(localworkflows.InitCodeWorkflow)

// init engine
err = engine.Init()
err = globalEngine.Init()
if err != nil {
globalLogger.Print("Failed to init Workflow Engine!", err)
return constants.SNYK_EXIT_CODE_ERROR
}

// add output flags as persistent flags
outputWorkflow, _ := engine.GetWorkflow(localworkflows.WORKFLOWID_OUTPUT_WORKFLOW)
outputWorkflow, _ := globalEngine.GetWorkflow(localworkflows.WORKFLOWID_OUTPUT_WORKFLOW)
outputFlags := workflow.FlagsetFromConfigurationOptions(outputWorkflow.GetConfigurationOptions())
rootCommand.PersistentFlags().AddFlagSet(outputFlags)

// add workflows as commands
createCommandsForWorkflows(rootCommand, engine)
createCommandsForWorkflows(rootCommand, globalEngine)

// init NetworkAccess
networkAccess := engine.GetNetworkAccess()
networkAccess := globalEngine.GetNetworkAccess()
networkAccess.AddHeaderField("x-snyk-cli-version", cliv2.GetFullVersion())
networkAccess.AddHeaderField(
"User-Agent",
Expand All @@ -415,7 +463,7 @@ func MainWithErrorCode() int {
}

// init Analytics
cliAnalytics := engine.GetAnalytics()
cliAnalytics := globalEngine.GetAnalytics()
cliAnalytics.SetVersion(cliv2.GetFullVersion())
cliAnalytics.SetCmdArguments(os.Args[1:])
cliAnalytics.SetOperatingSystem(internalOS)
Expand Down Expand Up @@ -443,7 +491,7 @@ func MainWithErrorCode() int {
cliAnalytics.AddError(err)
}

displayError(err)
displayError(err, os.Stdout, globalConfiguration)

exitCode := cliv2.DeriveExitCode(err)
globalLogger.Printf("Exiting with %d", exitCode)
Expand Down
Loading
Loading