diff --git a/cmd/syft/cli/attest/attest.go b/cmd/syft/cli/attest/attest.go index c0e202d1a..4a29d9b3e 100644 --- a/cmd/syft/cli/attest/attest.go +++ b/cmd/syft/cli/attest/attest.go @@ -98,11 +98,12 @@ func Run(ctx context.Context, app *config.Application, ko sign.KeyOpts, args []s eventBus := partybus.NewBus() stereoscope.SetBus(eventBus) syft.SetBus(eventBus) + subscription := eventBus.Subscribe() return eventloop.EventLoop( execWorker(app, *si, format, predicateType, sv), eventloop.SetupSignals(), - eventBus.Subscribe(), + subscription, stereoscope.Cleanup, ui.Select(options.IsVerbose(app), app.Quiet)..., ) diff --git a/cmd/syft/cli/packages/packages.go b/cmd/syft/cli/packages/packages.go index 74ca43410..55b797899 100644 --- a/cmd/syft/cli/packages/packages.go +++ b/cmd/syft/cli/packages/packages.go @@ -46,11 +46,12 @@ func Run(ctx context.Context, app *config.Application, args []string) error { eventBus := partybus.NewBus() stereoscope.SetBus(eventBus) syft.SetBus(eventBus) + subscription := eventBus.Subscribe() return eventloop.EventLoop( execWorker(app, *si, writer), eventloop.SetupSignals(), - eventBus.Subscribe(), + subscription, stereoscope.Cleanup, ui.Select(options.IsVerbose(app), app.Quiet)..., ) diff --git a/cmd/syft/cli/poweruser/poweruser.go b/cmd/syft/cli/poweruser/poweruser.go index 7faad4cce..1908a1650 100644 --- a/cmd/syft/cli/poweruser/poweruser.go +++ b/cmd/syft/cli/poweruser/poweruser.go @@ -53,11 +53,12 @@ func Run(ctx context.Context, app *config.Application, args []string) error { eventBus := partybus.NewBus() stereoscope.SetBus(eventBus) syft.SetBus(eventBus) + subscription := eventBus.Subscribe() return eventloop.EventLoop( execWorker(app, *si, writer), eventloop.SetupSignals(), - eventBus.Subscribe(), + subscription, stereoscope.Cleanup, ui.Select(options.IsVerbose(app), app.Quiet)..., ) diff --git a/test/cli/power_user_cmd_test.go b/test/cli/power_user_cmd_test.go index 8091ebd35..ac48ff150 100644 --- a/test/cli/power_user_cmd_test.go +++ b/test/cli/power_user_cmd_test.go @@ -92,7 +92,7 @@ func TestPowerUserCmdFlags(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - cmd, stdout, stderr := runSyft(t, test.env, test.args...) + cmd, stdout, stderr := runSyftSafe(t, test.env, test.args...) for _, traitFn := range test.assertions { traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode()) } diff --git a/test/cli/trait_assertions_test.go b/test/cli/trait_assertions_test.go index cdfd1ab9f..d14dfca73 100644 --- a/test/cli/trait_assertions_test.go +++ b/test/cli/trait_assertions_test.go @@ -152,7 +152,7 @@ func assertVerifyAttestation(coverageImage string) traitAssertion { coverageImage, // TODO which remote image to use? ) - stdout, stderr := runCommand(attachCmd, nil) + stdout, stderr, _ := runCommand(attachCmd, nil) if attachCmd.ProcessState.ExitCode() != 0 { tb.Log("STDOUT", stdout) tb.Log("STDERR", stderr) @@ -165,7 +165,7 @@ func assertVerifyAttestation(coverageImage string) traitAssertion { coverageImage, // TODO which remote image to use? ) - stdout, stderr = runCommand(verifyCmd, nil) + stdout, stderr, _ = runCommand(verifyCmd, nil) if attachCmd.ProcessState.ExitCode() != 0 { tb.Log("STDOUT", stdout) tb.Log("STDERR", stderr) diff --git a/test/cli/utils_test.go b/test/cli/utils_test.go index 094fce530..7897f38c6 100644 --- a/test/cli/utils_test.go +++ b/test/cli/utils_test.go @@ -2,7 +2,6 @@ package cli import ( "bytes" - "context" "fmt" "math" "os" @@ -11,12 +10,11 @@ import ( "path/filepath" "runtime" "strings" + "syscall" "testing" "time" - "github.com/anchore/stereoscope" "github.com/anchore/stereoscope/pkg/imagetest" - "github.com/stretchr/testify/require" ) func setupPKI(t *testing.T, pw string) func() { @@ -27,7 +25,7 @@ func setupPKI(t *testing.T, pw string) func() { cosignPath := filepath.Join(repoRoot(t), ".tmp/cosign") cmd := exec.Command(cosignPath, "generate-key-pair") - stdout, stderr := runCommand(cmd, nil) + stdout, stderr, _ := runCommand(cmd, nil) if cmd.ProcessState.ExitCode() != 0 { t.Log("STDOUT", stdout) t.Log("STDERR", stderr) @@ -54,25 +52,13 @@ func setupPKI(t *testing.T, pw string) func() { func getFixtureImage(t testing.TB, fixtureImageName string) string { t.Logf("obtaining fixture image for %s", fixtureImageName) - request := imagetest.PrepareFixtureImage(t, "docker-archive", fixtureImageName) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - i, err := stereoscope.GetImage(ctx, request) - t.Logf("got image %s: %s", fixtureImageName, i.Metadata.ID) - require.NoError(t, err) - t.Cleanup(func() { - require.NoError(t, i.Cleanup()) - }) - - path := imagetest.GetFixtureImageTarPath(t, fixtureImageName) - t.Logf("returning %s: %s", fixtureImageName, path) - return path + imagetest.GetFixtureImage(t, "docker-archive", fixtureImageName) + return imagetest.GetFixtureImageTarPath(t, fixtureImageName) } func pullDockerImage(t testing.TB, image string) { cmd := exec.Command("docker", "pull", image) - stdout, stderr := runCommand(cmd, nil) + stdout, stderr, _ := runCommand(cmd, nil) if cmd.ProcessState.ExitCode() != 0 { t.Log("STDOUT", stdout) t.Log("STDERR", stderr) @@ -95,15 +81,25 @@ func runSyftInDocker(t testing.TB, env map[string]string, image string, args ... args..., ) cmd := exec.Command("docker", allArgs...) - stdout, stderr := runCommand(cmd, env) + stdout, stderr, _ := runCommand(cmd, env) return cmd, stdout, stderr } func runSyft(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, string, string) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - cmd := exec.CommandContext(ctx, getSyftBinaryLocation(t), args...) + return runSyftCommand(t, env, true, args...) +} +func runSyftSafe(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, string, string) { + return runSyftCommand(t, env, false, args...) +} + +func runSyftCommand(t testing.TB, env map[string]string, expectError bool, args ...string) (*exec.Cmd, string, string) { + cancel := make(chan bool, 1) + defer func() { + cancel <- true + }() + + cmd := getSyftCommand(t, args...) if env == nil { env = make(map[string]string) } @@ -111,7 +107,45 @@ func runSyft(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, st // we should not have tests reaching out for app update checks env["SYFT_CHECK_FOR_APP_UPDATE"] = "false" - stdout, stderr := runCommand(cmd, env) + timeout := func() { + select { + case <-cancel: + return + case <-time.After(60 * time.Second): + } + + if cmd != nil && cmd.Process != nil { + // get a stack trace printed + err := cmd.Process.Signal(syscall.SIGABRT) + if err != nil { + t.Errorf("error aborting: %+v", err) + } + } + } + + go timeout() + + stdout, stderr, err := runCommand(cmd, env) + + if !expectError && err != nil && stdout == "" { + t.Errorf("error running syft: %+v", err) + t.Errorf("STDOUT: %s", stdout) + t.Errorf("STDERR: %s", stderr) + + // this probably indicates a timeout + args = append(args, "-vv") + cmd = getSyftCommand(t, args...) + + go timeout() + stdout, stderr, err = runCommand(cmd, env) + + if err != nil { + t.Errorf("error rerunning syft: %+v", err) + t.Errorf("STDOUT: %s", stdout) + t.Errorf("STDERR: %s", stderr) + } + } + return cmd, stdout, stderr } @@ -121,7 +155,12 @@ func runCosign(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, env = make(map[string]string) } - stdout, stderr := runCommand(cmd, env) + stdout, stderr, err := runCommand(cmd, env) + + if err != nil { + t.Errorf("error running cosign: %+v", err) + } + return cmd, stdout, stderr } @@ -129,7 +168,7 @@ func getCosignCommand(t testing.TB, args ...string) *exec.Cmd { return exec.Command(filepath.Join(repoRoot(t), ".tmp/cosign"), args...) } -func runCommand(cmd *exec.Cmd, env map[string]string) (string, string) { +func runCommand(cmd *exec.Cmd, env map[string]string) (string, string, error) { if env != nil { cmd.Env = append(os.Environ(), envMapToSlice(env)...) } @@ -138,9 +177,9 @@ func runCommand(cmd *exec.Cmd, env map[string]string) (string, string) { cmd.Stderr = &stderr // ignore errors since this may be what the test expects - cmd.Run() + err := cmd.Run() - return stdout.String(), stderr.String() + return stdout.String(), stderr.String(), err } func envMapToSlice(env map[string]string) (envList []string) { @@ -153,6 +192,10 @@ func envMapToSlice(env map[string]string) (envList []string) { return } +func getSyftCommand(t testing.TB, args ...string) *exec.Cmd { + return exec.Command(getSyftBinaryLocation(t), args...) +} + func getSyftBinaryLocation(t testing.TB) string { if os.Getenv("SYFT_BINARY_LOCATION") != "" { // SYFT_BINARY_LOCATION is the absolute path to the snapshot binary