From 941c830513ef085794dc45473104a457b5199d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Miri=C4=87?= Date: Tue, 25 Apr 2023 17:07:49 +0200 Subject: [PATCH] Add JS scenario options This is a quick and possibly naive change to support browser options per scenario (#3022). It doesn't try to enable generic options for any JS module as #3000 did, but it seems Ned abandoned this idea. We can open this discussion again once #883 is resolved. --- cmd/tests/cmd_run_test.go | 2 +- js/modules/k6/execution/execution_test.go | 2 +- lib/executor/base_config.go | 7 +++++++ lib/executor/executors_test.go | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/cmd/tests/cmd_run_test.go b/cmd/tests/cmd_run_test.go index b218af734169..5b1515d9ec5e 100644 --- a/cmd/tests/cmd_run_test.go +++ b/cmd/tests/cmd_run_test.go @@ -431,7 +431,7 @@ func TestExecutionTestOptionsDefaultValues(t *testing.T) { loglines := ts.LoggerHook.Drain() require.Len(t, loglines, 1) - expected := `{"paused":null,"executionSegment":null,"executionSegmentSequence":null,"noSetup":null,"setupTimeout":null,"noTeardown":null,"teardownTimeout":null,"rps":null,"dns":{"ttl":null,"select":null,"policy":null},"maxRedirects":null,"userAgent":null,"batch":null,"batchPerHost":null,"httpDebug":null,"insecureSkipTLSVerify":null,"tlsCipherSuites":null,"tlsVersion":null,"tlsAuth":null,"throw":null,"thresholds":null,"blacklistIPs":null,"blockHostnames":null,"hosts":null,"noConnectionReuse":null,"noVUConnectionReuse":null,"minIterationDuration":null,"ext":null,"summaryTrendStats":["avg", "min", "med", "max", "p(90)", "p(95)"],"summaryTimeUnit":null,"systemTags":["check","error","error_code","expected_response","group","method","name","proto","scenario","service","status","subproto","tls_version","url"],"tags":null,"metricSamplesBufferSize":null,"noCookiesReset":null,"discardResponseBodies":null,"consoleOutput":null,"scenarios":{"default":{"vus":null,"iterations":1,"executor":"shared-iterations","maxDuration":null,"startTime":null,"env":null,"tags":null,"gracefulStop":null,"exec":null}},"localIPs":null}` + expected := `{"paused":null,"executionSegment":null,"executionSegmentSequence":null,"noSetup":null,"setupTimeout":null,"noTeardown":null,"teardownTimeout":null,"rps":null,"dns":{"ttl":null,"select":null,"policy":null},"maxRedirects":null,"userAgent":null,"batch":null,"batchPerHost":null,"httpDebug":null,"insecureSkipTLSVerify":null,"tlsCipherSuites":null,"tlsVersion":null,"tlsAuth":null,"throw":null,"thresholds":null,"blacklistIPs":null,"blockHostnames":null,"hosts":null,"noConnectionReuse":null,"noVUConnectionReuse":null,"minIterationDuration":null,"ext":null,"summaryTrendStats":["avg", "min", "med", "max", "p(90)", "p(95)"],"summaryTimeUnit":null,"systemTags":["check","error","error_code","expected_response","group","method","name","proto","scenario","service","status","subproto","tls_version","url"],"tags":null,"metricSamplesBufferSize":null,"noCookiesReset":null,"discardResponseBodies":null,"consoleOutput":null,"scenarios":{"default":{"vus":null,"iterations":1,"executor":"shared-iterations","maxDuration":null,"options":{"browser":null},"startTime":null,"env":null,"tags":null,"gracefulStop":null,"exec":null}},"localIPs":null}` assert.JSONEq(t, expected, loglines[0].Message) } diff --git a/js/modules/k6/execution/execution_test.go b/js/modules/k6/execution/execution_test.go index 9ac9c8558687..8949eef2d175 100644 --- a/js/modules/k6/execution/execution_test.go +++ b/js/modules/k6/execution/execution_test.go @@ -190,7 +190,7 @@ func TestAbortTest(t *testing.T) { //nolint:tparallel func TestOptionsTestFull(t *testing.T) { t.Parallel() - expected := `{"paused":true,"scenarios":{"const-vus":{"executor":"constant-vus","startTime":"10s","gracefulStop":"30s","env":{"FOO":"bar"},"exec":"default","tags":{"tagkey":"tagvalue"},"vus":50,"duration":"10m0s"}},"executionSegment":"0:1/4","executionSegmentSequence":"0,1/4,1/2,1","noSetup":true,"setupTimeout":"1m0s","noTeardown":true,"teardownTimeout":"5m0s","rps":100,"dns":{"ttl":"1m","select":"roundRobin","policy":"any"},"maxRedirects":3,"userAgent":"k6-user-agent","batch":15,"batchPerHost":5,"httpDebug":"full","insecureSkipTLSVerify":true,"tlsCipherSuites":["TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"],"tlsVersion":{"min":"tls1.2","max":"tls1.3"},"tlsAuth":[{"domains":["example.com"],"cert":"mycert.pem","key":"mycert-key.pem","password":"mypwd"}],"throw":true,"thresholds":{"http_req_duration":[{"threshold":"rate>0.01","abortOnFail":true,"delayAbortEval":"10s"}]},"blacklistIPs":["192.0.2.0/24"],"blockHostnames":["test.k6.io","*.example.com"],"hosts":{"test.k6.io":"1.2.3.4:8443"},"noConnectionReuse":true,"noVUConnectionReuse":true,"minIterationDuration":"10s","ext":{"ext-one":{"rawkey":"rawvalue"}},"summaryTrendStats":["avg","min","max"],"summaryTimeUnit":"ms","systemTags":["iter","vu"],"tags":null,"metricSamplesBufferSize":8,"noCookiesReset":true,"discardResponseBodies":true,"consoleOutput":"loadtest.log","tags":{"runtag-key":"runtag-value"},"localIPs":"192.168.20.12-192.168.20.15,192.168.10.0/27"}` + expected := `{"paused":true,"scenarios":{"const-vus":{"executor":"constant-vus","options":{"browser":null},"startTime":"10s","gracefulStop":"30s","env":{"FOO":"bar"},"exec":"default","tags":{"tagkey":"tagvalue"},"vus":50,"duration":"10m0s"}},"executionSegment":"0:1/4","executionSegmentSequence":"0,1/4,1/2,1","noSetup":true,"setupTimeout":"1m0s","noTeardown":true,"teardownTimeout":"5m0s","rps":100,"dns":{"ttl":"1m","select":"roundRobin","policy":"any"},"maxRedirects":3,"userAgent":"k6-user-agent","batch":15,"batchPerHost":5,"httpDebug":"full","insecureSkipTLSVerify":true,"tlsCipherSuites":["TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"],"tlsVersion":{"min":"tls1.2","max":"tls1.3"},"tlsAuth":[{"domains":["example.com"],"cert":"mycert.pem","key":"mycert-key.pem","password":"mypwd"}],"throw":true,"thresholds":{"http_req_duration":[{"threshold":"rate>0.01","abortOnFail":true,"delayAbortEval":"10s"}]},"blacklistIPs":["192.0.2.0/24"],"blockHostnames":["test.k6.io","*.example.com"],"hosts":{"test.k6.io":"1.2.3.4:8443"},"noConnectionReuse":true,"noVUConnectionReuse":true,"minIterationDuration":"10s","ext":{"ext-one":{"rawkey":"rawvalue"}},"summaryTrendStats":["avg","min","max"],"summaryTimeUnit":"ms","systemTags":["iter","vu"],"tags":null,"metricSamplesBufferSize":8,"noCookiesReset":true,"discardResponseBodies":true,"consoleOutput":"loadtest.log","tags":{"runtag-key":"runtag-value"},"localIPs":"192.168.20.12-192.168.20.15,192.168.10.0/27"}` var ( rt = goja.New() diff --git a/lib/executor/base_config.go b/lib/executor/base_config.go index 5b3a47620276..9c20e9f91f61 100644 --- a/lib/executor/base_config.go +++ b/lib/executor/base_config.go @@ -20,6 +20,12 @@ var DefaultGracefulStopValue = 30 * time.Second //nolint:gochecknoglobals var executorNameWhitelist = regexp.MustCompile(`^[0-9a-zA-Z_-]+$`) //nolint:gochecknoglobals const executorNameErr = "the executor name should contain only numbers, latin letters, underscores, and dashes" +// ScenarioOptions are options specific to a scenario. These include k6 browser +// options, which are validated by the browser module, and not by k6 core. +type ScenarioOptions struct { + Browser map[string]any `json:"browser"` +} + // BaseConfig contains the common config fields for all executors type BaseConfig struct { Name string `json:"-"` // set via the JS object key @@ -29,6 +35,7 @@ type BaseConfig struct { Env map[string]string `json:"env"` Exec null.String `json:"exec"` // function name, externally validated Tags map[string]string `json:"tags"` + Options ScenarioOptions `json:"options"` // TODO: future extensions like distribution, others? } diff --git a/lib/executor/executors_test.go b/lib/executor/executors_test.go index 10e0eab7fc99..bb5155510df3 100644 --- a/lib/executor/executors_test.go +++ b/lib/executor/executors_test.go @@ -401,6 +401,20 @@ var configMapTestCases = []configMapTestCase{ {`{"varrival": {"executor": "ramping-arrival-rate", "preAllocatedVUs": 20, "maxVUs": 50, "stages": [{"duration": "5m", "target": 10}], "timeUnit": "-1s"}}`, exp{validationError: true}}, {`{"varrival": {"executor": "ramping-arrival-rate", "preAllocatedVUs": 30, "maxVUs": 20, "stages": [{"duration": "5m", "target": 10}]}}`, exp{validationError: true}}, // TODO: more tests of mixed executors and execution plans + + // scenario options + { + `{"ui": {"executor": "shared-iterations", "iterations": 22, "vus": 12, "maxDuration": "100s", "options": {"browser": {"someBrowserOption": true}}}}`, + exp{custom: func(t *testing.T, cm lib.ScenarioConfigs) { + require.Empty(t, cm["ui"].Validate()) + siCfg, ok := cm["ui"].(SharedIterationsConfig) + require.True(t, ok) + require.NotEmpty(t, siCfg.Options.Browser) + assert.EqualValues(t, true, siCfg.Options.Browser["someBrowserOption"]) + }}, + }, + // only the "browser" scenario option is supported + {`{"ui": {"executor": "shared-iterations", "iterations": 22, "vus": 12, "maxDuration": "100s", "options": {"unsupported": {}}}}`, exp{parseError: true}}, } func TestConfigMapParsingAndValidation(t *testing.T) {