/
docker.js
150 lines (125 loc) · 3.38 KB
/
docker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
const cp = require('child_process');
function runCmd(opts, containerId, cmd) {
console.log('[DEBUG withDocker] docker exec ' + containerId + ' ' + cmd);
cp.execSync('docker exec ' + containerId + ' ' + cmd, {
stdio: 'inherit',
});
process.stdout.write('\n');
}
const CONTAINERS = {};
function getContainers() {
console.log('Waiting for container to come up...');
const res = cp.execSync('docker ps');
return res.toString().split('\n').slice(1);
}
module.exports.withDocker = async function (opts, cb) {
outer: while (true) {
for (const container of getContainers()) {
const id = container.split(' ')[0];
if (CONTAINERS[opts.image] && CONTAINERS[opts.image].includes(id)) {
console.log('Waiting for existing container from this image to die.');
await new Promise((r) => setTimeout(r, 3000));
continue outer;
}
}
break;
}
const cmd = 'docker';
const args = ['run', '-d'];
if (opts.port) {
let port = String(opts.port);
if (!port.includes(':')) {
port = `${port}:${port}`;
}
args.push('-p', port);
}
if (opts.env) {
for (const [key, value] of Object.entries(opts.env)) {
args.push('-e', `${key}=${value}`);
}
}
if (opts.args) {
args.push(...opts.args);
}
args.push(opts.image);
if (opts.program) {
if (Array.isArray(opts.program)) {
args.push(...opts.program);
} else {
args.push(opts.program);
}
}
console.log(`[DEBUG withDocker] ${cmd} ${args.join(' ')}`);
const proc = cp.spawn(cmd, args);
let stdout = '';
proc.stdout.on('data', (m) => {
stdout += m.toString();
});
let stderr = '';
proc.stderr.on('data', (m) => {
stderr += m.toString();
});
proc.on('exit', (s) => {
if (s == '0') {
return;
}
console.log({ stdout, stderr });
process.exit(s);
});
let running = false;
while (!running) {
const containerId = stdout.slice(0, 12);
const containers = getContainers();
for (const container of containers) {
const id = container.split(' ')[0];
if (id && id == containerId) {
running = true;
}
}
await new Promise((r) => setTimeout(r, 1500));
}
const containerId = stdout.slice(0, 12);
if (!CONTAINERS[opts.image]) {
CONTAINERS[opts.image] = [];
}
CONTAINERS[opts.image].push(containerId);
try {
if (opts.wait) {
await opts.wait(containerId);
} else if (opts.cmds) {
let first = true;
while (true) {
if (!first) {
await new Promise((r) => setTimeout(r, 1500));
}
first = false;
try {
runCmd(opts, containerId, opts.cmds[0]);
break;
} catch (e) {
/* pass */
}
}
opts.cmds = opts.cmds.slice(1);
}
if (opts.cmds) {
for (const c of opts.cmds) {
runCmd(opts, containerId, c);
}
}
await cb();
} finally {
console.log('Killing container');
cp.execSync('docker kill ' + containerId, { stdio: 'inherit' });
console.log('Killed container');
CONTAINERS[opts.image] = CONTAINERS[opts.image].filter(
(c) => c === containerId
);
if (process.env.CI == 'true') {
// Clear up disk space if possible since Github Actions doesn't
// have a massive disk.
cp.execSync('docker image prune -a', { stdio: 'inherit' });
}
}
};
module.exports.DEFAULT_TIMEOUT = 360_000;