Skip to content

Commit 49eaea0

Browse files
[Backport 8.7] Fixes to YAML REST integration test suite runner (#1840)
Co-authored-by: Josh Mock <[email protected]>
1 parent c562a73 commit 49eaea0

File tree

5 files changed

+111
-27
lines changed

5 files changed

+111
-27
lines changed

.ci/run-elasticsearch.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
# - Use https only when TEST_SUITE is "platinum", when "free" use http
2323
# - Set xpack.security.enabled=false for "free" and xpack.security.enabled=true for "platinum"
2424

25-
script_path=$(dirname $(realpath -s $0))
26-
source $script_path/functions/imports.sh
25+
script_path=$(dirname "$(realpath -s "$0")")
26+
source "$script_path/functions/imports.sh"
2727
set -euo pipefail
2828

29-
echo -e "\033[34;1mINFO:\033[0m Take down node if called twice with the same arguments (DETACH=true) or on seperate terminals \033[0m"
30-
cleanup_node $es_node_name
29+
echo -e "\033[34;1mINFO:\033[0m Take down node if called twice with the same arguments (DETACH=true) or on separate terminals \033[0m"
30+
cleanup_node "$es_node_name"
3131

3232
master_node_name=${es_node_name}
3333
cluster_name=${moniker}${suffix}

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.PHONY: integration-setup
2+
integration-setup: integration-cleanup
3+
DETACH=true .ci/run-elasticsearch.sh
4+
5+
.PHONY: integration-cleanup
6+
integration-cleanup:
7+
docker stop instance || true
8+
docker volume rm instance-rest-test-data || true
9+
10+
.PHONY: integration
11+
integration: integration-setup
12+
npm run test:integration

scripts/utils/generateApis.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ function generateSingleApi (version, spec, common) {
228228
229229
${genUrlValidation(paths, api)}
230230
231-
let { ${genQueryBlacklist(false)}, ...querystring } = params
231+
let { ${genQueryDenylist(false)}, ...querystring } = params
232232
querystring = snakeCaseKeys(acceptedQuerystring, snakeCase, querystring)
233233
234234
let path = ''
@@ -316,20 +316,20 @@ function generateSingleApi (version, spec, common) {
316316
}, {})
317317
}
318318

319-
function genQueryBlacklist (addQuotes = true) {
319+
function genQueryDenylist (addQuotes = true) {
320320
const toCamelCase = str => {
321321
return str[0] === '_'
322322
? '_' + str.slice(1).replace(/_([a-z])/g, k => k[1].toUpperCase())
323323
: str.replace(/_([a-z])/g, k => k[1].toUpperCase())
324324
}
325325

326-
const blacklist = ['method', 'body']
326+
const denylist = ['method', 'body']
327327
parts.forEach(p => {
328328
const camelStr = toCamelCase(p)
329-
if (camelStr !== p) blacklist.push(`${camelStr}`)
330-
blacklist.push(`${p}`)
329+
if (camelStr !== p) denylist.push(`${camelStr}`)
330+
denylist.push(`${p}`)
331331
})
332-
return addQuotes ? blacklist.map(q => `'${q}'`) : blacklist
332+
return addQuotes ? denylist.map(q => `'${q}'`) : denylist
333333
}
334334

335335
function buildPath () {

test/integration/README.md

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
Yes.
66

77
## Background
8-
Elasticsearch offers its entire API via HTTP REST endpoints. You can find the whole API specification for every version [here](https://github.com/elastic/elasticsearch/tree/master/rest-api-spec/src/main/resources/rest-api-spec/api).<br/>
9-
To support different languages at the same time, the Elasticsearch team decided to provide a [YAML specification](https://github.com/elastic/elasticsearch/tree/master/rest-api-spec/src/main/resources/rest-api-spec/test) to test every endpoint, body, headers, warning, error and so on.<br/>
8+
Elasticsearch offers its entire API via HTTP REST endpoints. You can find the whole API specification for every version [here](https://github.com/elastic/elasticsearch/tree/main/rest-api-spec/src/main/resources/rest-api-spec/api).<br/>
9+
To support different languages at the same time, the Elasticsearch team decided to provide a [YAML specification](https://github.com/elastic/elasticsearch/tree/main/rest-api-spec/src/main/resources/rest-api-spec/test) to test every endpoint, body, headers, warning, error and so on.<br/>
1010
This testing suite uses that specification to generate the test for the specified version of Elasticsearch on the fly.
1111

1212
## Run
@@ -20,20 +20,45 @@ Once the Elasticsearch repository has been cloned, the testing suite will connec
2020

2121
The specification does not allow the test to be run in parallel, so it might take a while to run the entire testing suite; on my machine, `MacBookPro15,2 core i7 2.7GHz 16GB of RAM` it takes around four minutes.
2222

23+
### Running locally
24+
25+
If you want to run the integration tests on your development machine, you must have an Elasticsearch instance running first.
26+
A local instance can be spun up in a Docker container by running the [`.ci/run-elasticsearch.sh`](/.ci/run-elasticsearch.sh) script.
27+
This is the same script CI jobs use to run Elasticsearch for integration tests, so your results should be relatively consistent.
28+
29+
To simplify the process of starting a container, testing, and cleaning up the container, you can run the `make integration` target:
30+
31+
```sh
32+
# set some parameters
33+
export STACK_VERSION=8.7.0
34+
export TEST_SUITE=free # can be `free` or `platinum`
35+
make integration
36+
```
37+
38+
If Elasticsearch doesn't come up, run `make integration-cleanup` and then `DETACH=false .ci/run-elasticsearch.sh` manually to read the startup logs.
39+
40+
If you get an error about `vm.max_map_count` being too low, run `sudo sysctl -w vm.max_map_count=262144` to update the setting until the next reboot, or `sudo sysctl -w vm.max_map_count=262144 | sudo tee -a /etc/sysctl.conf` to update the setting permanently.
41+
2342
### Exit on the first failure
24-
Bu default the suite will run all the test, even if one assertion has failed. If you want to stop the test at the first failure, use the bailout option:
43+
44+
By default the suite will run all the tests, even if one assertion has failed. If you want to stop the test at the first failure, use the bailout option:
45+
2546
```sh
2647
npm run test:integration -- --bail
2748
```
2849

2950
### Calculate the code coverage
51+
3052
If you want to calculate the code coverage just run the testing suite with the following parameters, once the test ends, it will open a browser window with the results.
53+
3154
```sh
3255
npm run test:integration -- --cov --coverage-report=html
3356
```
3457

3558
## How does this thing work?
59+
3660
At first sight, it might seem complicated, but once you understand what the moving parts are, it's quite easy.
61+
3762
1. Connects to the given Elasticsearch instance
3863
1. Gets the ES version and build hash
3964
1. Checkout to the given hash (and clone the repository if it is not present)
@@ -46,7 +71,4 @@ At first sight, it might seem complicated, but once you understand what the movi
4671

4772
Inside the `index.js` file, you will find the connection, cloning, reading and parsing part of the test, while inside the `test-runner.js` file you will find the function to handle the assertions. Inside `test-runner.js`, we use a [queue](https://github.com/delvedor/workq) to be sure that everything is run in the correct order.
4873

49-
Checkout the [rest-api-spec readme](https://github.com/elastic/elasticsearch/blob/master/rest-api-spec/src/main/resources/rest-api-spec/test/README.asciidoc) if you want to know more about how the assertions work.
50-
51-
#### Why are we running the test with the `--harmony` flag?
52-
Because on Node v6 the regex lookbehinds are not supported.
74+
Check out the [rest-api-spec readme](https://github.com/elastic/elasticsearch/blob/main/rest-api-spec/src/main/resources/rest-api-spec/test/README.asciidoc) if you want to know more about how the assertions work.

test/integration/index.js

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ process.on('unhandledRejection', function (err) {
2727
const { writeFileSync, readFileSync, readdirSync, statSync } = require('fs')
2828
const { join, sep } = require('path')
2929
const yaml = require('js-yaml')
30+
const minimist = require('minimist')
3031
const ms = require('ms')
3132
const { Client } = require('../../index')
3233
const { kProductCheck } = require('@elastic/transport/lib/symbols')
@@ -42,12 +43,24 @@ const MAX_API_TIME = 1000 * 90
4243
const MAX_FILE_TIME = 1000 * 30
4344
const MAX_TEST_TIME = 1000 * 3
4445

46+
const options = minimist(process.argv.slice(2), {
47+
boolean: ['bail']
48+
})
49+
4550
const freeSkips = {
4651
// not supported yet
4752
'/free/cluster.desired_nodes/10_basic.yml': ['*'],
53+
54+
// Cannot find methods on `Internal` object
55+
'/free/cluster.desired_balance/10_basic.yml': ['*'],
56+
'/free/cluster.desired_nodes/20_dry_run.yml': ['*'],
57+
'/free/cluster.prevalidate_node_removal/10_basic.yml': ['*'],
58+
4859
'/free/health/30_feature.yml': ['*'],
4960
'/free/health/40_useractions.yml': ['*'],
50-
// the v8 client never sends the scroll_id in querystgring,
61+
'/free/health/40_diagnosis.yml': ['Diagnosis'],
62+
63+
// the v8 client never sends the scroll_id in querystring,
5164
// the way the test is structured causes a security exception
5265
'free/scroll/10_basic.yml': ['Body params override query string'],
5366
'free/scroll/11_clear.yml': [
@@ -56,80 +69,99 @@ const freeSkips = {
5669
],
5770
'free/cat.allocation/10_basic.yml': ['*'],
5871
'free/cat.snapshots/10_basic.yml': ['Test cat snapshots output'],
72+
5973
// TODO: remove this once 'arbitrary_key' is implemented
6074
// https://github.com/elastic/elasticsearch/pull/41492
6175
'indices.split/30_copy_settings.yml': ['*'],
6276
'indices.stats/50_disk_usage.yml': ['Disk usage stats'],
6377
'indices.stats/60_field_usage.yml': ['Field usage stats'],
78+
6479
// skipping because we are booting ES with `discovery.type=single-node`
6580
// and this test will fail because of this configuration
6681
'nodes.stats/30_discovery.yml': ['*'],
82+
6783
// the expected error is returning a 503,
6884
// which triggers a retry and the node to be marked as dead
6985
'search.aggregation/240_max_buckets.yml': ['*'],
86+
7087
// long values and json do not play nicely together
7188
'search.aggregation/40_range.yml': ['Min and max long range bounds'],
89+
7290
// the yaml runner assumes that null means "does not exists",
7391
// while null is a valid json value, so the check will fail
7492
'search/320_disallow_queries.yml': ['Test disallow expensive queries'],
75-
'free/tsdb/90_unsupported_operations.yml': ['noop update']
93+
'free/tsdb/90_unsupported_operations.yml': ['noop update'],
7694
}
77-
const platinumBlackList = {
95+
96+
const platinumDenyList = {
7897
'api_key/10_basic.yml': ['Test get api key'],
7998
'api_key/20_query.yml': ['*'],
8099
'api_key/11_invalidation.yml': ['Test invalidate api key by realm name'],
81100
'analytics/histogram.yml': ['Histogram requires values in increasing order'],
101+
82102
// this two test cases are broken, we should
83103
// return on those in the future.
84104
'analytics/top_metrics.yml': [
85105
'sort by keyword field fails',
86106
'sort by string script fails'
87107
],
108+
88109
'cat.aliases/10_basic.yml': ['Empty cluster'],
89110
'index/10_with_id.yml': ['Index with ID'],
90111
'indices.get_alias/10_basic.yml': ['Get alias against closed indices'],
91112
'indices.get_alias/20_empty.yml': ['Check empty aliases when getting all aliases via /_alias'],
92113
'text_structure/find_structure.yml': ['*'],
114+
93115
// https://github.com/elastic/elasticsearch/pull/39400
94116
'ml/jobs_crud.yml': ['Test put job with id that is already taken'],
117+
95118
// object keys must me strings, and `0.0.toString()` is `0`
96119
'ml/evaluate_data_frame.yml': [
97120
'Test binary_soft_classifition precision',
98121
'Test binary_soft_classifition recall',
99122
'Test binary_soft_classifition confusion_matrix'
100123
],
124+
101125
// it gets random failures on CI, must investigate
102126
'ml/set_upgrade_mode.yml': [
103127
'Attempt to open job when upgrade_mode is enabled',
104128
'Setting upgrade mode to disabled from enabled'
105129
],
130+
106131
// The cleanup fails with a index not found when retrieving the jobs
107132
'ml/get_datafeed_stats.yml': ['Test get datafeed stats when total_search_time_ms mapping is missing'],
108133
'ml/bucket_correlation_agg.yml': ['Test correlation bucket agg simple'],
134+
109135
// start should be a string
110136
'ml/jobs_get_result_overall_buckets.yml': ['Test overall buckets given epoch start and end params'],
137+
111138
// this can't happen with the client
112139
'ml/start_data_frame_analytics.yml': ['Test start with inconsistent body/param ids'],
113140
'ml/stop_data_frame_analytics.yml': ['Test stop with inconsistent body/param ids'],
114141
'ml/preview_datafeed.yml': ['*'],
142+
115143
// Investigate why is failing
116144
'ml/inference_crud.yml': ['*'],
117145
'ml/categorization_agg.yml': ['Test categorization aggregation with poor settings'],
118146
'ml/filter_crud.yml': ['*'],
147+
119148
// investigate why this is failing
120149
'monitoring/bulk/10_basic.yml': ['*'],
121150
'monitoring/bulk/20_privileges.yml': ['*'],
122151
'license/20_put_license.yml': ['*'],
123152
'snapshot/10_basic.yml': ['*'],
124153
'snapshot/20_operator_privileges_disabled.yml': ['*'],
154+
125155
// the body is correct, but the regex is failing
126156
'sql/sql.yml': ['Getting textual representation'],
127157
'searchable_snapshots/10_usage.yml': ['*'],
128158
'service_accounts/10_basic.yml': ['*'],
159+
129160
// we are setting two certificates in the docker config
130161
'ssl/10_basic.yml': ['*'],
131162
'token/10_basic.yml': ['*'],
132163
'token/11_invalidation.yml': ['*'],
164+
133165
// very likely, the index template has not been loaded yet.
134166
// we should run a indices.existsTemplate, but the name of the
135167
// template may vary during time.
@@ -147,16 +179,20 @@ const platinumBlackList = {
147179
'transforms_stats.yml': ['*'],
148180
'transforms_stats_continuous.yml': ['*'],
149181
'transforms_update.yml': ['*'],
182+
150183
// js does not support ulongs
151184
'unsigned_long/10_basic.yml': ['*'],
152185
'unsigned_long/20_null_value.yml': ['*'],
153186
'unsigned_long/30_multi_fields.yml': ['*'],
154187
'unsigned_long/40_different_numeric.yml': ['*'],
155188
'unsigned_long/50_script_values.yml': ['*'],
189+
156190
// the v8 client flattens the body into the parent object
157191
'platinum/users/10_basic.yml': ['Test put user with different username in body'],
192+
158193
// docker issue?
159194
'watcher/execute_watch/60_http_input.yml': ['*'],
195+
160196
// the checks are correct, but for some reason the test is failing on js side
161197
// I bet is because the backslashes in the rg
162198
'watcher/execute_watch/70_invalid.yml': ['*'],
@@ -170,8 +206,16 @@ const platinumBlackList = {
170206
'platinum/ml/delete_job_force.yml': ['Test force delete an open job that is referred by a started datafeed'],
171207
'platinum/ml/evaluate_data_frame.yml': ['*'],
172208
'platinum/ml/get_datafeed_stats.yml': ['*'],
209+
173210
// start should be a string in the yaml test
174-
'platinum/ml/start_stop_datafeed.yml': ['*']
211+
'platinum/ml/start_stop_datafeed.yml': ['*'],
212+
213+
// health API not yet supported
214+
'/platinum/health/10_usage.yml': ['*'],
215+
216+
// ML update_trained_model_deployment not supported yet
217+
'/platinum/ml/3rd_party_deployment.yml': ['Test update deployment'],
218+
'/platinum/ml/update_trained_model_deployment.yml': ['Test with unknown model id']
175219
}
176220

177221
function runner (opts = {}) {
@@ -316,7 +360,12 @@ async function start ({ client, isXPack }) {
316360
junitTestSuites.end()
317361
generateJunitXmlReport(junit, isXPack ? 'platinum' : 'free')
318362
console.error(err)
319-
process.exit(1)
363+
364+
if (options.bail) {
365+
process.exit(1)
366+
} else {
367+
continue
368+
}
320369
}
321370
const totalTestTime = now() - testTime
322371
junitTestCase.end()
@@ -380,7 +429,8 @@ function generateJunitXmlReport (junit, suite) {
380429
}
381430

382431
if (require.main === module) {
383-
const node = process.env.TEST_ES_SERVER || 'http://elastic:changeme@localhost:9200'
432+
const scheme = process.env.TEST_SUITE === 'platinum' ? 'https' : 'http'
433+
const node = process.env.TEST_ES_SERVER || `${scheme}://elastic:changeme@localhost:9200`
384434
const opts = {
385435
node,
386436
isXPack: process.env.TEST_SUITE !== 'free'
@@ -395,20 +445,20 @@ const shouldSkip = (isXPack, file, name) => {
395445
for (let j = 0; j < freeTest.length; j++) {
396446
if (file.endsWith(list[i]) && (name === freeTest[j] || freeTest[j] === '*')) {
397447
const testName = file.slice(file.indexOf(`${sep}elasticsearch${sep}`)) + ' / ' + name
398-
log(`Skipping test ${testName} because is blacklisted in the free test`)
448+
log(`Skipping test ${testName} because it is denylisted in the free test suite`)
399449
return true
400450
}
401451
}
402452
}
403453

404454
if (file.includes('x-pack') || isXPack) {
405-
list = Object.keys(platinumBlackList)
455+
list = Object.keys(platinumDenyList)
406456
for (let i = 0; i < list.length; i++) {
407-
const platTest = platinumBlackList[list[i]]
457+
const platTest = platinumDenyList[list[i]]
408458
for (let j = 0; j < platTest.length; j++) {
409459
if (file.endsWith(list[i]) && (name === platTest[j] || platTest[j] === '*')) {
410460
const testName = file.slice(file.indexOf(`${sep}elasticsearch${sep}`)) + ' / ' + name
411-
log(`Skipping test ${testName} because is blacklisted in the platinum test`)
461+
log(`Skipping test ${testName} because it is denylisted in the platinum test suite`)
412462
return true
413463
}
414464
}

0 commit comments

Comments
 (0)