Skip to content

Commit cba4aff

Browse files
committed
Move integration test to artifact API (#1436)
1 parent 3d2720d commit cba4aff

File tree

4 files changed

+175
-107
lines changed

4 files changed

+175
-107
lines changed

.ci/Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ FROM node:${NODE_JS_VERSION}
44
# Create app directory
55
WORKDIR /usr/src/app
66

7+
RUN apt-get clean -y
8+
RUN apt-get update -y
9+
RUN apt-get install -y zip
10+
711
# Install app dependencies
812
COPY package*.json ./
913
RUN npm install

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"@sinonjs/fake-timers": "github:sinonjs/fake-timers#0bfffc1",
5151
"@types/node": "^14.14.28",
5252
"convert-hrtime": "^3.0.0",
53+
"cross-zip": "^4.0.0",
5354
"dedent": "^0.7.0",
5455
"deepmerge": "^4.2.2",
5556
"dezalgo": "^1.0.3",
@@ -58,6 +59,7 @@
5859
"js-yaml": "^4.0.0",
5960
"license-checker": "^25.0.1",
6061
"minimist": "^1.2.5",
62+
"node-fetch": "^2.6.1",
6163
"ora": "^5.3.0",
6264
"pretty-hrtime": "^1.0.3",
6365
"proxy": "^1.0.2",
@@ -102,4 +104,4 @@
102104
"coverage": false,
103105
"jobs-auto": true
104106
}
105-
}
107+
}

scripts/download-artifacts.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
'use strict'
2+
3+
const { join } = require('path')
4+
const minimist = require('minimist')
5+
const stream = require('stream')
6+
const { promisify } = require('util')
7+
const { createWriteStream, promises } = require('fs')
8+
const rimraf = require('rimraf')
9+
const fetch = require('node-fetch')
10+
const crossZip = require('cross-zip')
11+
const ora = require('ora')
12+
13+
const { mkdir, writeFile } = promises
14+
const pipeline = promisify(stream.pipeline)
15+
const unzip = promisify(crossZip.unzip)
16+
const rm = promisify(rimraf)
17+
18+
const esFolder = join(__dirname, '..', 'elasticsearch')
19+
const zipFolder = join(esFolder, 'artifacts.zip')
20+
const specFolder = join(esFolder, 'rest-api-spec', 'api')
21+
const freeTestFolder = join(esFolder, 'rest-api-spec', 'test', 'free')
22+
const xPackTestFolder = join(esFolder, 'rest-api-spec', 'test', 'platinum')
23+
const artifactInfo = join(esFolder, 'info.json')
24+
25+
async function downloadArtifacts (opts) {
26+
if (typeof opts.version !== 'string') {
27+
throw new Error('Missing version')
28+
}
29+
30+
const log = ora('Checking out spec and test').start()
31+
32+
log.text = 'Resolving versions'
33+
let resolved
34+
try {
35+
resolved = await resolve(opts.version, opts.hash)
36+
} catch (err) {
37+
log.fail(err.message)
38+
process.exit(1)
39+
}
40+
41+
opts.id = opts.id || resolved.id
42+
opts.hash = opts.hash || resolved.hash
43+
opts.version = resolved.version
44+
45+
const info = loadInfo()
46+
47+
if (info && info.version === opts.version) {
48+
if (info.hash === opts.hash && info.id === opts.id) {
49+
log.succeed('The artifact copy present locally is already up to date')
50+
return
51+
}
52+
}
53+
54+
log.text = 'Cleanup checkouts/elasticsearch'
55+
await rm(esFolder)
56+
await mkdir(esFolder, { recursive: true })
57+
58+
log.text = 'Downloading artifacts'
59+
const response = await fetch(resolved.url)
60+
if (!response.ok) {
61+
log.fail(`unexpected response ${response.statusText}`)
62+
process.exit(1)
63+
}
64+
await pipeline(response.body, createWriteStream(zipFolder))
65+
66+
log.text = 'Unzipping'
67+
await unzip(zipFolder, esFolder)
68+
69+
log.text = 'Cleanup'
70+
await rm(zipFolder)
71+
72+
log.text = 'Update info'
73+
await writeFile(artifactInfo, JSON.stringify(opts), 'utf8')
74+
75+
log.succeed('Done')
76+
}
77+
78+
function loadInfo () {
79+
try {
80+
return require(artifactInfo)
81+
} catch (err) {
82+
return null
83+
}
84+
}
85+
86+
async function resolve (version, hash) {
87+
const response = await fetch(`https://artifacts-api.elastic.co/v1/versions/${version}`)
88+
if (!response.ok) {
89+
throw new Error(`unexpected response ${response.statusText}`)
90+
}
91+
92+
const data = await response.json()
93+
const esBuilds = data.version.builds
94+
.filter(build => build.projects.elasticsearch != null)
95+
.map(build => {
96+
return {
97+
projects: build.projects.elasticsearch,
98+
buildId: build.build_id,
99+
date: build.start_time,
100+
version: build.version
101+
}
102+
})
103+
.sort((a, b) => {
104+
const dA = new Date(a.date)
105+
const dB = new Date(b.date)
106+
if (dA > dB) return -1
107+
if (dA < dB) return 1
108+
return 0
109+
})
110+
111+
if (hash != null) {
112+
const build = esBuilds.find(build => build.projects.commit_hash === hash)
113+
if (!build) {
114+
throw new Error(`Can't find any build with hash '${hash}'`)
115+
}
116+
const zipKey = Object.keys(build.projects.packages).find(key => key.startsWith('rest-resources-zip-') && key.endsWith('.zip'))
117+
return {
118+
url: build.projects.packages[zipKey].url,
119+
id: build.buildId,
120+
hash: build.projects.commit_hash,
121+
version: build.version
122+
}
123+
}
124+
125+
const lastBuild = esBuilds[0]
126+
const zipKey = Object.keys(lastBuild.projects.packages).find(key => key.startsWith('rest-resources-zip-') && key.endsWith('.zip'))
127+
return {
128+
url: lastBuild.projects.packages[zipKey].url,
129+
id: lastBuild.buildId,
130+
hash: lastBuild.projects.commit_hash,
131+
version: lastBuild.version
132+
}
133+
}
134+
135+
async function main (options) {
136+
delete options._
137+
await downloadArtifacts(options)
138+
}
139+
if (require.main === module) {
140+
process.on('unhandledRejection', function (err) {
141+
console.error(err)
142+
process.exit(1)
143+
})
144+
145+
const options = minimist(process.argv.slice(2), {
146+
string: ['id', 'version', 'hash']
147+
})
148+
main(options).catch(t => {
149+
console.log(t)
150+
process.exit(2)
151+
})
152+
}
153+
154+
module.exports = downloadArtifacts
155+
module.exports.locations = {
156+
specFolder,
157+
freeTestFolder,
158+
xPackTestFolder
159+
}

test/integration/index.js

Lines changed: 9 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,18 @@ process.on('unhandledRejection', function (err) {
2424
process.exit(1)
2525
})
2626

27-
const { writeFileSync, readFileSync, accessSync, mkdirSync, readdirSync, statSync } = require('fs')
27+
const { writeFileSync, readFileSync, readdirSync, statSync } = require('fs')
2828
const { join, sep } = require('path')
2929
const yaml = require('js-yaml')
30-
const Git = require('simple-git')
3130
const ms = require('ms')
3231
const { Client } = require('../../index')
3332
const build = require('./test-runner')
3433
const { sleep } = require('./helper')
3534
const createJunitReporter = require('./reporter')
35+
const downloadArtifacts = require('../../scripts/download-artifacts')
3636

37-
const esRepo = 'https://github.com/elastic/elasticsearch.git'
38-
const esFolder = join(__dirname, '..', '..', 'elasticsearch')
39-
const yamlFolder = join(esFolder, 'rest-api-spec', 'src', 'main', 'resources', 'rest-api-spec', 'test')
40-
const xPackYamlFolder = join(esFolder, 'x-pack', 'plugin', 'src', 'test', 'resources', 'rest-api-spec', 'test')
37+
const yamlFolder = downloadArtifacts.locations.freeTestFolder
38+
const xPackYamlFolder = downloadArtifacts.locations.xPackTestFolder
4139

4240
const MAX_API_TIME = 1000 * 90
4341
const MAX_FILE_TIME = 1000 * 30
@@ -83,6 +81,7 @@ const platinumBlackList = {
8381
],
8482
// The cleanup fails with a index not found when retrieving the jobs
8583
'ml/get_datafeed_stats.yml': ['Test get datafeed stats when total_search_time_ms mapping is missing'],
84+
'ml/preview_datafeed.yml': ['*'],
8685
// Investigate why is failing
8786
'ml/inference_crud.yml': ['*'],
8887
// investigate why this is failing
@@ -168,10 +167,10 @@ async function start ({ client, isXPack }) {
168167
await waitCluster(client)
169168

170169
const { body } = await client.info()
171-
const { number: version, build_hash: sha } = body.version
170+
const { number: version, build_hash: hash } = body.version
172171

173-
log(`Checking out sha ${sha}...`)
174-
await withSHA(sha)
172+
log(`Downloading artifacts for hash ${hash}...`)
173+
await downloadArtifacts({ hash, version })
175174

176175
log(`Testing ${isXPack ? 'Platinum' : 'Free'} api...`)
177176
const junit = createJunitReporter()
@@ -216,7 +215,7 @@ async function start ({ client, isXPack }) {
216215
const testRunner = build({
217216
client,
218217
version,
219-
isXPack: file.includes('x-pack')
218+
isXPack: file.includes('platinum')
220219
})
221220
const fileTime = now()
222221
const data = readFileSync(file, 'utf8')
@@ -324,102 +323,6 @@ function parse (data) {
324323
return doc
325324
}
326325

327-
/**
328-
* Sets the elasticsearch repository to the given sha.
329-
* If the repository is not present in `esFolder` it will
330-
* clone the repository and the checkout the sha.
331-
* If the repository is already present but it cannot checkout to
332-
* the given sha, it will perform a pull and then try again.
333-
* @param {string} sha
334-
* @param {function} callback
335-
*/
336-
function withSHA (sha) {
337-
return new Promise((resolve, reject) => {
338-
_withSHA(err => err ? reject(err) : resolve())
339-
})
340-
341-
function _withSHA (callback) {
342-
let fresh = false
343-
let retry = 0
344-
345-
if (!pathExist(esFolder)) {
346-
if (!createFolder(esFolder)) {
347-
return callback(new Error('Failed folder creation'))
348-
}
349-
fresh = true
350-
}
351-
352-
const git = Git(esFolder)
353-
354-
if (fresh) {
355-
clone(checkout)
356-
} else {
357-
checkout()
358-
}
359-
360-
function checkout () {
361-
log(`Checking out sha '${sha}'`)
362-
git.checkout(sha, err => {
363-
if (err) {
364-
if (retry++ > 0) {
365-
return callback(err)
366-
}
367-
return pull(checkout)
368-
}
369-
callback()
370-
})
371-
}
372-
373-
function pull (cb) {
374-
log('Pulling elasticsearch repository...')
375-
git.pull(err => {
376-
if (err) {
377-
return callback(err)
378-
}
379-
cb()
380-
})
381-
}
382-
383-
function clone (cb) {
384-
log('Cloning elasticsearch repository...')
385-
git.clone(esRepo, esFolder, err => {
386-
if (err) {
387-
return callback(err)
388-
}
389-
cb()
390-
})
391-
}
392-
}
393-
}
394-
395-
/**
396-
* Checks if the given path exists
397-
* @param {string} path
398-
* @returns {boolean} true if exists, false if not
399-
*/
400-
function pathExist (path) {
401-
try {
402-
accessSync(path)
403-
return true
404-
} catch (err) {
405-
return false
406-
}
407-
}
408-
409-
/**
410-
* Creates the given folder
411-
* @param {string} name
412-
* @returns {boolean} true on success, false on failure
413-
*/
414-
function createFolder (name) {
415-
try {
416-
mkdirSync(name)
417-
return true
418-
} catch (err) {
419-
return false
420-
}
421-
}
422-
423326
function generateJunitXmlReport (junit, suite) {
424327
writeFileSync(
425328
join(__dirname, '..', '..', `${suite}-report-junit.xml`),

0 commit comments

Comments
 (0)