Skip to content

Commit

Permalink
Validate threshold configuration before starting the execution
Browse files Browse the repository at this point in the history
This commit makes sure that the threshold configuration (as passed in
the script exported options for instance) is valid, before starting the
execution.

A valid threshold must pass the following assertion:
- Its expression is syntaxically correct and is parsable
- It applies to a metrics that's known to k6, either builtin or custom
- Its expression's aggregation method is valid for the metric it applies
to

Threshold validation will be made in the context of the `run`, `cloud`,
`archive`, `inspect`, and `archive` commands. If a threshold definition
is invalid, the k6 program will exit with
a status code of 104.

ref #2330
  • Loading branch information
oleiade committed Feb 3, 2022
1 parent d857e83 commit 3aee96d
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 5 deletions.
2 changes: 1 addition & 1 deletion cmd/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ An archive is a fully self-contained test run, and can be executed identically e
return err
}

_, err = deriveAndValidateConfig(conf, r.IsExecutable, logger)
_, err = deriveAndValidateConfig(conf, registry, r.IsExecutable, logger)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ This will execute the test on the k6 cloud service. Use "k6 login cloud" to auth
return err
}

derivedConf, err := deriveAndValidateConfig(conf, r.IsExecutable, logger)
derivedConf, err := deriveAndValidateConfig(conf, registry, r.IsExecutable, logger)
if err != nil {
return err
}
Expand Down
1 change: 0 additions & 1 deletion cmd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"go.k6.io/k6/lib"
"go.k6.io/k6/lib/executor"
"go.k6.io/k6/lib/metrics"
"go.k6.io/k6/lib/testutils"
"go.k6.io/k6/lib/types"
"go.k6.io/k6/stats"
"gopkg.in/guregu/null.v3"
Expand Down
2 changes: 1 addition & 1 deletion cmd/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func addExecRequirements(b *js.Bundle,
return nil, err
}

conf, err = deriveAndValidateConfig(conf, runner.IsExecutable, logger)
conf, err = deriveAndValidateConfig(conf, registry, runner.IsExecutable, logger)
if err != nil {
return nil, err
}
Expand Down
7 changes: 6 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import (
"go.k6.io/k6/lib/consts"
"go.k6.io/k6/lib/metrics"
"go.k6.io/k6/loader"
"go.k6.io/k6/stats"
"go.k6.io/k6/ui/pb"
)

Expand Down Expand Up @@ -110,6 +111,10 @@ a commandline interface for interacting with it.`,
builtinMetrics := metrics.RegisterBuiltinMetrics(registry)
initRunner, err := newRunner(logger, src, globalFlags.runType, filesystems, runtimeOptions, builtinMetrics, registry)
if err != nil {
if errors.Is(err, stats.ErrThresholdParsing) {
return errext.WithExitCodeIfNone(err, exitcodes.InvalidConfig)
}

return common.UnwrapGojaInterruptedError(err)
}

Expand All @@ -125,7 +130,7 @@ a commandline interface for interacting with it.`,
return err
}

conf, err = deriveAndValidateConfig(conf, initRunner.IsExecutable, logger)
conf, err = deriveAndValidateConfig(conf, registry, initRunner.IsExecutable, logger)
if err != nil {
return err
}
Expand Down
11 changes: 11 additions & 0 deletions cmd/testdata/thresholds/malformed_expression.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const options = {
thresholds: {
http_reqs: ["foo>0"], // Counter
},
};

export default function () {
console.log(
"asserting that a malformed threshold fails with exit code 104 (Invalid config)"
);
}
13 changes: 13 additions & 0 deletions cmd/testdata/thresholds/non_existing_metric.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const options = {
thresholds: {
// non existing is neither registered, nor a builtin metric.
// k6 should catch that.
"non existing": ["rate>0"],
},
};

export default function () {
console.log(
"asserting that a threshold over a non-existing metric fails with exit code 104 (Invalid config)"
);
}
15 changes: 15 additions & 0 deletions cmd/testdata/thresholds/unsupported_aggregation_method.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const options = {
thresholds: {
// http_reqs is a Counter metric. As such, it supports
// only the 'count' and 'rate' operations. Thus, 'value'
// being a Gauge's metric aggregation method, the threshold
// configuration evaluation should fail.
http_reqs: ["value>0"],
},
};

export default function () {
console.log(
"asserting that a threshold applying a method over a metric not supporting it fails with exit code 104 (Invalid config)"
);
}

0 comments on commit 3aee96d

Please sign in to comment.