diff --git a/cmd/run.go b/cmd/run.go index 60f9bdd136d..652de3717e4 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -208,7 +208,7 @@ func (c *cmdRun) run(cmd *cobra.Command, args []string) error { err = engineRun() if err != nil { err = common.UnwrapGojaInterruptedError(err) - if common.IsInterruptError(err) { + if errext.IsInterruptError(err) { // Don't return here since we need to work with --linger, // show the end-of-test summary and exit cleanly. interrupt = err diff --git a/cmd/run_test.go b/cmd/run_test.go index b97a55e03e1..3c8360e695a 100644 --- a/cmd/run_test.go +++ b/cmd/run_test.go @@ -39,7 +39,6 @@ import ( "go.k6.io/k6/errext" "go.k6.io/k6/errext/exitcodes" - "go.k6.io/k6/js/common" "go.k6.io/k6/lib/fsext" "go.k6.io/k6/lib/testutils" ) @@ -138,27 +137,27 @@ func TestRunScriptErrorsAndAbort(t *testing.T) { testCases := []struct { testFilename, name string expErr, expLogOutput string - expExitCode errext.ExitCode + expExitCode exitcodes.ExitCode extraArgs []string }{ { testFilename: "abort.js", - expErr: common.AbortTest, + expErr: errext.AbortTest, expExitCode: exitcodes.ScriptAborted, }, { testFilename: "abort_initerr.js", - expErr: common.AbortTest, + expErr: errext.AbortTest, expExitCode: exitcodes.ScriptAborted, }, { testFilename: "abort_initvu.js", - expErr: common.AbortTest, + expErr: errext.AbortTest, expExitCode: exitcodes.ScriptAborted, }, { testFilename: "abort_teardown.js", - expErr: common.AbortTest, + expErr: errext.AbortTest, expExitCode: exitcodes.ScriptAborted, expLogOutput: "Calling teardown function after test.abort()", }, @@ -246,7 +245,7 @@ func TestInvalidOptionsThresholdErrExitCode(t *testing.T) { testCases := []struct { name string testFilename string - expExitCode errext.ExitCode + expExitCode exitcodes.ExitCode extraArgs []string }{ { diff --git a/core/engine.go b/core/engine.go index bd428374050..7e9cf96156d 100644 --- a/core/engine.go +++ b/core/engine.go @@ -29,7 +29,6 @@ import ( "github.com/sirupsen/logrus" "go.k6.io/k6/errext" - "go.k6.io/k6/js/common" "go.k6.io/k6/lib" "go.k6.io/k6/metrics" "go.k6.io/k6/metrics/engine" @@ -196,7 +195,7 @@ func (e *Engine) startBackgroundProcesses( switch { case errors.As(err, &serr): e.OutputManager.SetRunStatus(lib.RunStatusAbortedScriptError) - case common.IsInterruptError(err): + case errext.IsInterruptError(err): e.OutputManager.SetRunStatus(lib.RunStatusAbortedUser) default: e.OutputManager.SetRunStatus(lib.RunStatusAbortedSystem) diff --git a/core/local/local.go b/core/local/local.go index 62482d82d82..926c297fe71 100644 --- a/core/local/local.go +++ b/core/local/local.go @@ -30,7 +30,6 @@ import ( "github.com/sirupsen/logrus" "go.k6.io/k6/errext" - "go.k6.io/k6/js/common" "go.k6.io/k6/lib" "go.k6.io/k6/lib/executor" "go.k6.io/k6/metrics" @@ -485,7 +484,7 @@ func (e *ExecutionScheduler) Run(globalCtx, runCtx context.Context, engineOut ch return err } } - if err := executor.CancelReason(execCtx); err != nil && common.IsInterruptError(err) { + if err := executor.CancelReason(execCtx); err != nil && errext.IsInterruptError(err) { interrupted = true return err } diff --git a/errext/errext_test.go b/errext/errext_test.go index 486e1de26c7..47e1eac24d5 100644 --- a/errext/errext_test.go +++ b/errext/errext_test.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.k6.io/k6/errext/exitcodes" ) func assertHasHint(t *testing.T, err error, hint string) { @@ -36,7 +37,7 @@ func assertHasHint(t *testing.T, err error, hint string) { assert.Contains(t, err.Error(), typederr.Error()) } -func assertHasExitCode(t *testing.T, err error, exitcode ExitCode) { +func assertHasExitCode(t *testing.T, err error, exitcode exitcodes.ExitCode) { var typederr HasExitCode require.ErrorAs(t, err, &typederr) assert.Equal(t, typederr.ExitCode(), exitcode) @@ -46,7 +47,7 @@ func assertHasExitCode(t *testing.T, err error, exitcode ExitCode) { func TestErrextHelpers(t *testing.T) { t.Parallel() - const testExitCode ExitCode = 13 + const testExitCode exitcodes.ExitCode = 13 assert.Nil(t, WithHint(nil, "test hint")) assert.Nil(t, WithExitCodeIfNone(nil, testExitCode)) @@ -63,7 +64,7 @@ func TestErrextHelpers(t *testing.T) { assertHasHint(t, errWithExitCode, "better hint (test hint)") assertHasExitCode(t, errWithExitCode, testExitCode) - errWithExitCodeAgain := WithExitCodeIfNone(errWithExitCode, ExitCode(27)) + errWithExitCodeAgain := WithExitCodeIfNone(errWithExitCode, exitcodes.ExitCode(27)) assertHasHint(t, errWithExitCodeAgain, "better hint (test hint)") assertHasExitCode(t, errWithExitCodeAgain, testExitCode) diff --git a/errext/exit_code.go b/errext/exit_code.go index d42d52dd064..93b9db6522c 100644 --- a/errext/exit_code.go +++ b/errext/exit_code.go @@ -20,24 +20,27 @@ package errext -import "errors" +import ( + "errors" + + "go.k6.io/k6/errext/exitcodes" +) // ExitCode is the code with which the application should exit if this error // bubbles up to the top of the scope. Values should be between 0 and 125: // https://unix.stackexchange.com/questions/418784/what-is-the-min-and-max-values-of-exit-codes-in-linux -type ExitCode uint8 // HasExitCode is a wrapper around an error with an attached exit code. type HasExitCode interface { error - ExitCode() ExitCode + ExitCode() exitcodes.ExitCode } // WithExitCodeIfNone can attach an exit code to the given error, if it doesn't // have one already. It won't do anything if the error already had an exit code // attached. Similarly, if there is no error (i.e. the given error is nil), it // also won't do anything. -func WithExitCodeIfNone(err error, exitCode ExitCode) error { +func WithExitCodeIfNone(err error, exitCode exitcodes.ExitCode) error { if err == nil { // No error, do nothing return nil @@ -52,14 +55,14 @@ func WithExitCodeIfNone(err error, exitCode ExitCode) error { type withExitCode struct { error - exitCode ExitCode + exitCode exitcodes.ExitCode } func (wh withExitCode) Unwrap() error { return wh.error } -func (wh withExitCode) ExitCode() ExitCode { +func (wh withExitCode) ExitCode() exitcodes.ExitCode { return wh.exitCode } diff --git a/errext/exitcodes/codes.go b/errext/exitcodes/codes.go index c0e0b48c5ee..d7ed6be213a 100644 --- a/errext/exitcodes/codes.go +++ b/errext/exitcodes/codes.go @@ -22,19 +22,21 @@ //nolint: golint package exitcodes -import "go.k6.io/k6/errext" +// ExitCode is just a type representing a process exit code for k6 +type ExitCode uint8 +// list of exit codes used by k6 const ( - CloudTestRunFailed errext.ExitCode = 97 // This used to be 99 before k6 v0.33.0 - CloudFailedToGetProgress errext.ExitCode = 98 - ThresholdsHaveFailed errext.ExitCode = 99 - SetupTimeout errext.ExitCode = 100 - TeardownTimeout errext.ExitCode = 101 - GenericTimeout errext.ExitCode = 102 // TODO: remove? - GenericEngine errext.ExitCode = 103 - InvalidConfig errext.ExitCode = 104 - ExternalAbort errext.ExitCode = 105 - CannotStartRESTAPI errext.ExitCode = 106 - ScriptException errext.ExitCode = 107 - ScriptAborted errext.ExitCode = 108 + CloudTestRunFailed ExitCode = 97 // This used to be 99 before k6 v0.33.0 + CloudFailedToGetProgress ExitCode = 98 + ThresholdsHaveFailed ExitCode = 99 + SetupTimeout ExitCode = 100 + TeardownTimeout ExitCode = 101 + GenericTimeout ExitCode = 102 // TODO: remove? + GenericEngine ExitCode = 103 + InvalidConfig ExitCode = 104 + ExternalAbort ExitCode = 105 + CannotStartRESTAPI ExitCode = 106 + ScriptException ExitCode = 107 + ScriptAborted ExitCode = 108 ) diff --git a/errext/interrupt_error.go b/errext/interrupt_error.go new file mode 100644 index 00000000000..637622daed2 --- /dev/null +++ b/errext/interrupt_error.go @@ -0,0 +1,36 @@ +package errext + +import ( + "errors" + + "go.k6.io/k6/errext/exitcodes" +) + +// InterruptError is an error that halts engine execution +type InterruptError struct { + Reason string +} + +var _ HasExitCode = &InterruptError{} + +// Error returns the reason of the interruption. +func (i *InterruptError) Error() string { + return i.Reason +} + +// ExitCode returns the status code used when the k6 process exits. +func (i *InterruptError) ExitCode() exitcodes.ExitCode { + return exitcodes.ScriptAborted +} + +// AbortTest is the reason emitted when a test script calls test.abort() +const AbortTest = "test aborted" + +// IsInterruptError returns true if err is *InterruptError. +func IsInterruptError(err error) bool { + if err == nil { + return false + } + var intErr *InterruptError + return errors.As(err, &intErr) +} diff --git a/js/common/interrupt_error.go b/js/common/interrupt_error.go index eac3273afd8..1e4628c6e82 100644 --- a/js/common/interrupt_error.go +++ b/js/common/interrupt_error.go @@ -24,39 +24,8 @@ import ( "errors" "github.com/dop251/goja" - "go.k6.io/k6/errext" - "go.k6.io/k6/errext/exitcodes" ) -// InterruptError is an error that halts engine execution -type InterruptError struct { - Reason string -} - -var _ errext.HasExitCode = &InterruptError{} - -// Error returns the reason of the interruption. -func (i *InterruptError) Error() string { - return i.Reason -} - -// ExitCode returns the status code used when the k6 process exits. -func (i *InterruptError) ExitCode() errext.ExitCode { - return exitcodes.ScriptAborted -} - -// AbortTest is the reason emitted when a test script calls test.abort() -const AbortTest = "test aborted" - -// IsInterruptError returns true if err is *InterruptError. -func IsInterruptError(err error) bool { - if err == nil { - return false - } - var intErr *InterruptError - return errors.As(err, &intErr) -} - // UnwrapGojaInterruptedError returns the internal error handled by goja. func UnwrapGojaInterruptedError(err error) error { var gojaErr *goja.InterruptedError diff --git a/js/common/util.go b/js/common/util.go index 8c994ec2423..6ee93651932 100644 --- a/js/common/util.go +++ b/js/common/util.go @@ -29,6 +29,7 @@ import ( "github.com/dop251/goja" "github.com/sirupsen/logrus" + "go.k6.io/k6/errext" ) // Throw a JS error; avoids re-wrapping GoErrors. @@ -89,7 +90,7 @@ func RunWithPanicCatching(logger logrus.FieldLogger, rt *goja.Runtime, fn func() if r := recover(); r != nil { gojaStack := rt.CaptureCallStack(20, nil) - err = &InterruptError{Reason: fmt.Sprintf("a panic occurred during JS execution: %s", r)} + err = &errext.InterruptError{Reason: fmt.Sprintf("a panic occurred during JS execution: %s", r)} // TODO figure out how to use PanicLevel without panicing .. this might require changing // the logger we use see // https://github.com/sirupsen/logrus/issues/1028 diff --git a/js/modules/k6/execution/execution.go b/js/modules/k6/execution/execution.go index ea31e72d901..140ad568f88 100644 --- a/js/modules/k6/execution/execution.go +++ b/js/modules/k6/execution/execution.go @@ -29,6 +29,7 @@ import ( "github.com/dop251/goja" + "go.k6.io/k6/errext" "go.k6.io/k6/js/common" "go.k6.io/k6/js/modules" "go.k6.io/k6/lib" @@ -176,11 +177,11 @@ func (mi *ModuleInstance) newTestInfo() (*goja.Object, error) { // stop the test run "abort": func() interface{} { return func(msg goja.Value) { - reason := common.AbortTest + reason := errext.AbortTest if msg != nil && !goja.IsUndefined(msg) { reason = fmt.Sprintf("%s: %s", reason, msg.String()) } - rt.Interrupt(&common.InterruptError{Reason: reason}) + rt.Interrupt(&errext.InterruptError{Reason: reason}) } }, "options": func() interface{} { diff --git a/js/modules/k6/execution/execution_test.go b/js/modules/k6/execution/execution_test.go index 0ac78b5f215..b95877a49a7 100644 --- a/js/modules/k6/execution/execution_test.go +++ b/js/modules/k6/execution/execution_test.go @@ -14,6 +14,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.k6.io/k6/errext" "go.k6.io/k6/js/common" "go.k6.io/k6/js/modulestest" "go.k6.io/k6/lib" @@ -197,16 +198,16 @@ func TestAbortTest(t *testing.T) { //nolint:tparallel require.NotNil(t, err) var x *goja.InterruptedError assert.ErrorAs(t, err, &x) - v, ok := x.Value().(*common.InterruptError) + v, ok := x.Value().(*errext.InterruptError) require.True(t, ok) require.Equal(t, v.Reason, reason) } t.Run("default reason", func(t *testing.T) { //nolint: paralleltest - prove(t, "exec.test.abort()", common.AbortTest) + prove(t, "exec.test.abort()", errext.AbortTest) }) t.Run("custom reason", func(t *testing.T) { //nolint: paralleltest - prove(t, `exec.test.abort("mayday")`, fmt.Sprintf("%s: mayday", common.AbortTest)) + prove(t, `exec.test.abort("mayday")`, fmt.Sprintf("%s: mayday", errext.AbortTest)) }) } diff --git a/js/runner.go b/js/runner.go index f7899603a4b..1eadacedab6 100644 --- a/js/runner.go +++ b/js/runner.go @@ -751,7 +751,7 @@ func (u *ActiveVU) RunOnce() error { if err != nil { var x *goja.InterruptedError if errors.As(err, &x) { - if v, ok := x.Value().(*common.InterruptError); ok { + if v, ok := x.Value().(*errext.InterruptError); ok { v.Reason = x.Error() err = v } @@ -872,6 +872,6 @@ func (s *scriptException) Hint() string { return "script exception" } -func (s *scriptException) ExitCode() errext.ExitCode { +func (s *scriptException) ExitCode() exitcodes.ExitCode { return exitcodes.ScriptException } diff --git a/js/runner_test.go b/js/runner_test.go index c5af80621c1..c76df711552 100644 --- a/js/runner_test.go +++ b/js/runner_test.go @@ -48,7 +48,7 @@ import ( "go.k6.io/k6/core" "go.k6.io/k6/core/local" - "go.k6.io/k6/js/common" + "go.k6.io/k6/errext" "go.k6.io/k6/js/modules/k6" k6http "go.k6.io/k6/js/modules/k6/http" k6metrics "go.k6.io/k6/js/modules/k6/metrics" @@ -1690,7 +1690,7 @@ func TestInitContextForbidden(t *testing.T) { `var test = require("k6/execution").test; test.abort(); exports.default = function() { console.log("p"); }`, - common.AbortTest, + errext.AbortTest, }, { "group", diff --git a/js/timeout_error.go b/js/timeout_error.go index 9342883b287..5b8479605a4 100644 --- a/js/timeout_error.go +++ b/js/timeout_error.go @@ -65,7 +65,7 @@ func (t timeoutError) Hint() string { } // ExitCode returns the coresponding exit code value to the place. -func (t timeoutError) ExitCode() errext.ExitCode { +func (t timeoutError) ExitCode() exitcodes.ExitCode { // TODO: add handleSummary() switch t.place { case consts.SetupFn: diff --git a/lib/executor/helpers.go b/lib/executor/helpers.go index b435266f284..bafb66636dd 100644 --- a/lib/executor/helpers.go +++ b/lib/executor/helpers.go @@ -30,7 +30,6 @@ import ( "github.com/sirupsen/logrus" "go.k6.io/k6/errext" - "go.k6.io/k6/js/common" "go.k6.io/k6/lib" "go.k6.io/k6/lib/types" "go.k6.io/k6/ui/pb" @@ -125,7 +124,7 @@ func CancelReason(ctx context.Context) error { // cancels the executor context passed with ctx. func handleInterrupt(ctx context.Context, err error) bool { if err != nil { - if common.IsInterruptError(err) { + if errext.IsInterruptError(err) { cancelExecutorContext(ctx, err) return true }