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

Add abortTest() helper function #1920

Closed
wants to merge 33 commits into from

Conversation

gernest
Copy link
Contributor

@gernest gernest commented Mar 22, 2021

Closes #1001
This adds abortTest() helper function to the k6 module. This function when
called inside a script it will

  • stop the whole test run and k6 will exit with 107 status code
  • stops immediately the VU that called it and no more iterations are started
  • make sure the teardown() is called
  • the engine run status is 7 (RunStatusAbortedScriptError)

(*goja.Runtime).Interrupt is used for halting script execution and capturing
stack traces for better error message of what is happening with the script.

We introduce InterruptError which is used with (*goja.Runtime).Interrupt to
identify interrupts emitted by abortTest(). This way we use special handling of
this type.

Example script is

import { abortTest, sleep } from 'k6';

export default function () {
    // We abort the test on second iteration
    if (__ITER == 1) {
        abortTest();
    }
    sleep(1);
}

export function teardown() {
    console.log('This function will be called even when we abort script');
}

abortTest() can be called anywhere in the script.

The following script will fail with the error

ERRO[0000] Using abortTest() in the init context is not supported at (...path to the script )init.js:13:43(34)
import {
    abortTest
} from 'k6';

abortTest();

export function setup() {
}

export default function () {
    // ... some test logic ...
    console.log('mayday, mayday');
}

You can customize the reason for abortTest() by passing values to the function

abortTest("Exceeded expectations");

Will emit "Exceeded expectations" on the logs

Missing

  • Tests
    • Check status code is 107 when the abortTest() is called in a script
    • Wait for CTRL+C when --linger flag is set
    • Check correct default log message for abortTest() without arguments
    • Check correct custom log message like abortTest("maybday!")
    • Check teardown is called when a script aborts

Closes grafana#1001
This adds abortTest() helper function to the k6 module. This function when
called inside a script it will

- stop the whole test run and k6 will exit with 107 status code
- stops immediately the VU that called it  and no more iterations are started
- make sure the teardown() is called
- the engine run status is 7 (RunStatusAbortedScriptError)

`(*goja.Runtime).Interrupt` is used for halting script execution and capturing
stack traces for better error message of what is happening with the script.

We introduce InterruptError which is used with `(*goja.Runtime).Interrupt` to
identify interrupts emitted by abortTest(). This way we use special handling of
this type.

Example script is

```js
import { abortTest, sleep } from 'k6';

export default function () {
    // We abort the test on second iteration
    if (__ITER == 1) {
        abortTest();
    }
    sleep(1);
}

export function teardown() {
    console.log('This function will be called even when we abort script');
}
```

abortTest() can be called in both default and setup functions, however you can't use it
in the init context

The following script will fail with the error
```
ERRO[0000] Using abortTest() in the init context is not supported at (...path to the script )init.js:13:43(34)
```

```js
import {
    abortTest
} from 'k6';

abortTest();

export function setup() {
}

export default function () {
    // ... some test logic ...
    console.log('mayday, mayday');
}
```

You can customize the reason for abortTest() by passing values to the function

```js
abortTest("Exceeded expectations");
```

Will emit `"Exceeded expectations"` on the logs
@CLAassistant
Copy link

CLAassistant commented Mar 22, 2021

CLA assistant check
All committers have signed the CLA.

@codecov-io
Copy link

codecov-io commented Mar 22, 2021

Codecov Report

Merging #1920 (d7e8deb) into master (3abd529) will increase coverage by 1.78%.
The diff coverage is 86.00%.

❗ Current head d7e8deb differs from pull request most recent head a425d48. Consider uploading reports for the commit a425d48 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1920      +/-   ##
==========================================
+ Coverage   71.26%   73.04%   +1.78%     
==========================================
  Files         183      183              
  Lines       14336    14382      +46     
==========================================
+ Hits        10216    10505     +289     
+ Misses       3480     3206     -274     
- Partials      640      671      +31     
Flag Coverage Δ
ubuntu 73.04% <86.00%> (+1.80%) ⬆️
windows ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
cmd/run.go 60.33% <55.55%> (+48.36%) ⬆️
lib/errors/interrupt_error.go 60.00% <60.00%> (ø)
lib/executor/helpers.go 96.03% <94.44%> (-0.35%) ⬇️
core/engine.go 88.88% <100.00%> (+3.75%) ⬆️
core/local/local.go 81.69% <100.00%> (+5.97%) ⬆️
js/modules/k6/k6.go 100.00% <100.00%> (ø)
js/runner.go 81.71% <100.00%> (+0.84%) ⬆️
js/common/initenv.go 86.66% <0.00%> (-13.34%) ⬇️
js/initcontext.go 90.10% <0.00%> (-2.20%) ⬇️
cmd/ui_windows.go
... and 14 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3abd529...a425d48. Read the comment docs.

Copy link
Member

@na-- na-- left a comment

Choose a reason for hiding this comment

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

Thanks! I haven't done a full code review yet, I've only skimmed the changes and played around with the resulting binary a bit, but in general, your approach looks good! The only big issue I noticed is that these changes break the --linger functionality.

k6 run --linger script.js should still linger when abortTest() is used, i.e. the k6 process shouldn't exit directly after the test run has stopped. This behavior should remain the same as it was with the old way of aborting the test run (thresholds with abortOnFail: true).

This  change makes abortTest() behave like thresholds with `abortOnFail: true`
when called with --linger flag
@gernest
Copy link
Contributor Author

gernest commented Mar 23, 2021

@na-- thanks for the quick and good feedback. I had missed the --linger flag, but the docs were informative so I was able to address the flag use case.

When we run scripts that use abortTest() with --linger , basically every thing described on the PR message above stays the same except we don't immediately exit with 107 code .

Winding down of the engine will be observed like with thresholds and waiting for CTRL+C which will then allow us to exit with 107.

Relevant commit c07af18

Copy link
Contributor

@imiric imiric left a comment

Choose a reason for hiding this comment

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

This looks very good to me, nice work! I just left some minor comments and nitpicks.

Also there's a check next to "Check teardown is called when a script aborts", but I don't see that test and I agree that it should be added.

cmd/run.go Outdated Show resolved Hide resolved
cmd/run.go Outdated Show resolved Hide resolved
js/common/interupt_error.go Outdated Show resolved Hide resolved
js/common/interupt_error.go Outdated Show resolved Hide resolved
js/modules/k6/k6.go Outdated Show resolved Hide resolved
js/modules/k6/k6.go Outdated Show resolved Hide resolved
lib/executor/helpers.go Outdated Show resolved Hide resolved
lib/executor/helpers.go Outdated Show resolved Hide resolved
lib/executor/helpers.go Outdated Show resolved Hide resolved
Copy link
Contributor

@mstoykov mstoykov left a comment

Choose a reason for hiding this comment

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

Seems good in general 🎉

  • definitely, need to move the IterruptedError definition out of js/common and probably ... lib/errors, something else ?
  • not so certain that this isn't an abuse of contexts and that I wouldn't prefer a more direct approach, but looking at the code around this ... I think that will require some pretty big refactoring and probably will make things actually worse than better, so I am fine with it

cmd/run.go Outdated Show resolved Hide resolved
js/common/interupt_error.go Outdated Show resolved Hide resolved
js/modules/k6/k6.go Outdated Show resolved Hide resolved
lib/executor/helpers.go Outdated Show resolved Hide resolved
Comment on lines 154 to 157
if handleInterupt(ctx, err) {
executionState.AddInterruptedIterations(1)
return false
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the code nothing uses the return value. This seems to not matter as the contexts are getting checked constantly, but it is probably a good idea to figure out if we need to use it somehow

cc @imiric @na--

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, we might make a small PR to refactor this, but it might be needed eventually (e.g. in #1250 or #877) and is so small that it's probably fine as it is.

* master:
  Fix DOCKER_IMAGE_ID and add GHCR_IMAGE_ID after repo move
  Drop all the go mod replaces
  Bump golang version to 1.16 in Dockerfile
  Bump versions in CI from 1.14->1.15 and from 1.15->1.16
  Disable let/const babel plugin and co
  update goja to latest master
to avoid panics and data races pass empty flag address
@gernest gernest changed the title [WIP] Add abortTest() helper function Add abortTest() helper function Mar 29, 2021
@gernest gernest marked this pull request as ready for review March 29, 2021 15:27
@gernest
Copy link
Contributor Author

gernest commented Mar 29, 2021

@na-- @imiric @mstoykov thanks a lot for the amazing feedback, I have addressed all review comments (I think) PTAL

Also there's a check next to "Check teardown is called when a script aborts", but I don't see that test and I agree that it should be added.

Thanks for catching this one, I added the test in b5b8441 (that i improved a bit in later commits, like cancel context .. etc)

Copy link
Member

@na-- na-- left a comment

Choose a reason for hiding this comment

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

LGTM! The only minor nitpick I have is that instead of directly type casting errors, you should probably use the recent errors.Is() and errors.As() stdlib functions.

Other than that, everything looks great! The changes ended up more complex than I expected, but can't think of a way to simplify them without significant refactoring and everything is still understandable, so this is going to be it! 🎉

Copy link
Contributor

@imiric imiric left a comment

Choose a reason for hiding this comment

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

LGTM, well done! Just left a tiny nitpick, feel free to disregard.

Also, please rebase on top of the latest master and cleanup the commits a bit before this is merged. Not necessarily squashing to a single commit, but whatever makes sense for the changes to be atomic.

Comment on lines +139 to +141
cmd.Flags().AddFlag(&pflag.Flag{
Name: "address",
})
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this for?

...

Ah, I see, it's to avoid a nil pointer panic in run.go:217. It doesn't seem to be required for this test, but the one below does fail without it. In any case, it would be good to mention this in a comment.

Also, both of these tests are quite noisy when ran with go test -v. It would be good if that could be captured internally and not actually printed to stdout. Though if it requires overriding the global stdout var, then it's probably not worth it 😄

@rushby
Copy link

rushby commented Jun 17, 2021

Hi all,

Just been looking into this functionality would be great if this can get merged in soon!

@na--
Copy link
Member

na-- commented Jun 18, 2021

@rushby, we wanted to merge this for k6 v0.33.0, but we weren't able because it's blocked by some other changes we needed to make in k6 (e.g. #1863) and in our cloud, so it can work both in single instance and in distributed tests. We plan to merge it for k6 v0.34.0, slated to be released sometime in the end of August/early September.

@imiric
Copy link
Contributor

imiric commented Oct 12, 2021

Closing this in favor of #2093.

We changed the API slightly but you'll get full credits @gernest. Thanks!

@imiric imiric closed this Oct 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide an API to exit a test
7 participants