Skip to content

Commit 7af38e0

Browse files
committed
Promisify boot
1 parent 30c34ec commit 7af38e0

File tree

3 files changed

+85
-138
lines changed

3 files changed

+85
-138
lines changed

app.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ const machines = require('./lib/machines');
1515
const routes = require('./lib/routes');
1616
const users = require('./lib/users');
1717

18-
boot.executeInParallel([
18+
Promise.all([
1919
boot.forwardHttp,
2020
boot.ensureHttpsCertificates,
2121
boot.ensureDockerTlsCertificates
22-
], () => {
22+
]).then(() => {
2323
// You can customize these values in './db.json'.
2424
const hostname = db.get('hostname', 'localhost');
2525
const https = db.get('https');

join.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ if (!hostname || hostname === 'localhost') {
2424

2525
log('[ok] will try to join cluster as [hostname = ' + hostname + ']');
2626

27-
boot.executeInParallel([
28-
boot.forwardHttp,
29-
boot.ensureHttpsCertificates,
30-
boot.ensureDockerTlsCertificates,
31-
boot.verifyJanitorOAuth2Access
32-
], () => {
33-
boot.registerDockerClient(() => {
27+
Promise.all([
28+
boot.forwardHttp(),
29+
boot.ensureHttpsCertificates(),
30+
boot.ensureDockerTlsCertificates(),
31+
boot.verifyJanitorOAuth2Access()
32+
])
33+
.then(() => boot.registerDockerClient())
34+
.then(() => {
3435
log('[ok] joined cluster as [hostname = ' + hostname + ']');
3536

3637
const https = db.get('https');
@@ -80,7 +81,6 @@ boot.executeInParallel([
8081
});
8182
});
8283
});
83-
});
8484

8585
// Associate some non-persistent data to sessions.
8686
const oauth2States = {};

lib/boot.js

Lines changed: 75 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
const fs = require('fs');
55
const http = require('http');
6+
const {promisify} = require('util');
7+
const chmod = promisify(fs.chmod);
8+
const readFile = promisify(fs.readFile);
9+
const writeFile = promisify(fs.writeFile);
610

711
const certificates = require('./certificates');
812
const db = require('./db');
@@ -11,26 +15,8 @@ const oauth2 = require('./oauth2');
1115

1216
const hostname = db.get('hostname', 'localhost');
1317

14-
// Run given tasks in parrallel, and only call `next()` when all have succeeded.
15-
exports.executeInParallel = function (tasks, next) {
16-
let complete = 0;
17-
18-
for (const task of tasks) {
19-
try {
20-
task(() => {
21-
complete++;
22-
if (complete === tasks.length) {
23-
next();
24-
}
25-
});
26-
} catch (error) {
27-
log('[fail] boot task', error);
28-
}
29-
}
30-
};
31-
3218
// Permanently redirect all HTTP requests to HTTPS.
33-
exports.forwardHttp = function (next) {
19+
exports.forwardHttp = function () {
3420
const ports = db.get('ports');
3521
if (!ports.http || !ports.https) {
3622
// Use `make ports` to set up this unprivileged HTTP port.
@@ -55,17 +41,14 @@ exports.forwardHttp = function (next) {
5541
response.end();
5642
});
5743

58-
forwarder.listen(ports.http, () => {
59-
log('[ok] forwarding http:// → https://');
60-
next();
61-
});
44+
const listen = promisify(forwarder.listen);
45+
return listen(ports.http);
6246
};
6347

6448
// Verify HTTPS certificates and generate new ones if necessary.
65-
exports.ensureHttpsCertificates = function (next) {
49+
exports.ensureHttpsCertificates = async function () {
6650
if (db.get('security').forceHttp) {
6751
log('[warning] skipped https credentials verification');
68-
next();
6952
return;
7053
}
7154

@@ -79,7 +62,6 @@ exports.ensureHttpsCertificates = function (next) {
7962

8063
if (valid) {
8164
log('[ok] verified https credentials');
82-
next();
8365
return;
8466
}
8567

@@ -100,38 +82,34 @@ exports.ensureHttpsCertificates = function (next) {
10082
}
10183

10284
log('requesting new https credentials…');
103-
certificates.createHTTPSCertificate(parameters)
104-
.then(({ certificate, accountKey }) => {
105-
https.ca = [ certificate.ca ];
85+
return certificates.createHTTPSCertificate(parameters)
86+
.then(({certificate, accountKey}) => {
87+
https.ca = [certificate.ca];
10688
https.crt = certificate.cert;
10789
https.key = certificate.privkey;
10890
letsencrypt.key = accountKey;
10991
db.save();
11092
log('[ok] new https credentials installed');
111-
next();
112-
})
113-
.catch(error => {
114-
log('[fail] letsencrypt', error);
11593
});
11694
};
11795

11896
// Verify Docker TLS certificates, generate new ones if necessary.
119-
exports.ensureDockerTlsCertificates = function (next) {
97+
exports.ensureDockerTlsCertificates = async function () {
12098
// Read all TLS certificates.
12199
const tls = db.get('tls');
122100
const ca = tls.ca || {};
123101
const client = tls.client || {};
124102
const server = {};
125103

126104
try {
127-
server.ca = fs.readFileSync('./docker.ca', 'utf8');
128-
server.crt = fs.readFileSync('./docker.crt', 'utf8');
129-
server.key = fs.readFileSync('./docker.key', 'utf8');
105+
server.ca = await readFile('./docker.ca', 'utf8');
106+
server.crt = await readFile('./docker.crt', 'utf8');
107+
server.key = await readFile('./docker.key', 'utf8');
130108
} catch (error) {
131109
if (error.code !== 'ENOENT') {
132110
log('[fail] could not read docker-tls certificates', error);
133-
return;
134111
}
112+
throw error;
135113
}
136114

137115
let caValid = certificates.isValid({
@@ -142,42 +120,41 @@ exports.ensureDockerTlsCertificates = function (next) {
142120
let serverValid = false;
143121

144122
if (!caValid) {
145-
resetAllCertificates();
123+
await resetAllCertificates();
146124
return;
147125
}
148126

149127
clientValid = certificates.isValid({
150-
ca: [ ca.crt ],
128+
ca: [ca.crt],
151129
crt: client.crt,
152130
key: client.key
153131
});
154132

155133
serverValid = certificates.isValid({
156-
ca: [ ca.crt ],
134+
ca: [ca.crt],
157135
crt: server.crt,
158136
key: server.key,
159137
hostname: hostname
160138
}) && (server.ca.trim() === ca.crt.trim());
161139

162140
if (caValid && clientValid && serverValid) {
163141
log('[ok] verified docker-tls credentials');
164-
next();
165142
return;
166143
}
167144

168145
if (!clientValid) {
169-
resetClientCertificate();
146+
await resetClientCertificate();
170147
}
171148

172149
if (!serverValid) {
173-
resetServerCertificate();
150+
await resetServerCertificate();
174151
}
175152

176153
// Task: Reset the TLS certificate authority, then all depending certificates.
177-
function resetAllCertificates () {
154+
async function resetAllCertificates () {
178155
const parameters = {
179156
commonName: 'ca',
180-
basicConstraints: { cA: true },
157+
basicConstraints: {cA: true},
181158
keyUsage: {
182159
keyCertSign: true,
183160
digitalSignature: true,
@@ -186,16 +163,13 @@ exports.ensureDockerTlsCertificates = function (next) {
186163
};
187164

188165
log('generating new docker-tls certificate authority…');
189-
certificates.createTLSCertificate(parameters).then(({ crt, key }) => {
190-
ca.crt = crt;
191-
ca.key = key;
192-
tls.ca = ca;
193-
caValid = true;
194-
resetClientCertificate();
195-
resetServerCertificate();
196-
}).catch(error => {
197-
log('[fail] tls', error);
198-
});
166+
const {crt, key} = await certificates.createTLSCertificate(parameters);
167+
ca.crt = crt;
168+
ca.key = key;
169+
tls.ca = ca;
170+
caValid = true;
171+
await resetClientCertificate();
172+
await resetServerCertificate();
199173
}
200174

201175
// Task: Reset the TLS client certificate.
@@ -210,103 +184,80 @@ exports.ensureDockerTlsCertificates = function (next) {
210184
};
211185

212186
log('generating new docker-tls client certificate…');
213-
certificates.createTLSCertificate(parameters).then(({ crt, key }) => {
187+
return certificates.createTLSCertificate(parameters).then(({crt, key}) => {
214188
client.crt = crt;
215189
client.key = key;
216190
tls.client = client;
217191
clientValid = true;
218-
done();
219-
}).catch(error => {
220-
log('[fail] tls', error);
221192
});
222193
}
223194

224195
// Task: Reset the TLS server certificate.
225-
function resetServerCertificate () {
196+
async function resetServerCertificate () {
226197
const parameters = {
227198
commonName: hostname,
228-
altNames: [ 'localhost' ],
199+
altNames: ['localhost'],
229200
caCrt: ca.crt,
230201
caKey: ca.key
231202
};
232203

233204
log('generating new docker-tls server certificate…');
234-
certificates.createTLSCertificate(parameters).then(({ crt, key }) => {
235-
server.crt = crt;
236-
server.key = key;
237-
const filesToWrite = {
238-
'./docker.ca': ca.crt,
239-
'./docker.crt': server.crt,
240-
'./docker.key': server.key
241-
};
242-
243-
for (const file in filesToWrite) {
244-
const path = file;
245-
const value = filesToWrite[path];
246-
fs.writeFile(path, value, (error) => {
247-
if (error) {
248-
log('[fail] unable to write ' + path, error);
249-
return;
250-
}
251-
252-
fs.chmod(path, 0o600 /* read + write by owner */, (error) => {
253-
if (error) {
254-
log('[fail] unable to protect ' + path, error);
255-
return;
256-
}
257-
258-
delete filesToWrite[path];
259-
if (Object.keys(filesToWrite).length === 0) {
260-
// FIXME: Can we force the docker daemon to restart here, or to
261-
// switch certificates? Maybe we can do something like this:
262-
// `exec('sudo service docker restart')` ?
263-
log('[fail] please manually restart the docker daemon');
264-
// But continue anyway.
265-
serverValid = true;
266-
done();
267-
}
268-
});
269-
});
205+
const {crt, key} = await certificates.createTLSCertificate(parameters);
206+
server.crt = crt;
207+
server.key = key;
208+
const filesToWrite = {
209+
'./docker.ca': ca.crt,
210+
'./docker.crt': server.crt,
211+
'./docker.key': server.key
212+
};
213+
214+
for (const file in filesToWrite) {
215+
const path = file;
216+
const value = filesToWrite[path];
217+
try {
218+
await writeFile(path, value);
219+
} catch (error) {
220+
log('[fail] unable to write ' + path, error);
221+
throw error;
222+
}
223+
try {
224+
await chmod(path, 0o600 /* read + write by owner */);
225+
} catch (error) {
226+
log('[fail] unable to protect ' + path, error);
227+
throw error;
270228
}
271-
}).catch(error => {
272-
log('[fail] tls', error);
273-
});
274-
}
275229

276-
// Wait for all required tasks to finish before proceeding.
277-
function done () {
278-
if (!caValid || !clientValid || !serverValid) {
279-
// Some tasks are not finished yet. Let's wait.
280-
return;
230+
delete filesToWrite[path];
231+
if (Object.keys(filesToWrite).length === 0) {
232+
// FIXME: Can we force the docker daemon to restart here, or to
233+
// switch certificates? Maybe we can do something like this:
234+
// `exec('sudo service docker restart')` ?
235+
log('[warning] please manually restart the docker daemon');
236+
// But continue anyway.
237+
serverValid = true;
238+
}
281239
}
282-
283-
// eslint-disable-next-line no-func-assign
284-
done = null;
285-
db.save();
286-
log('[ok] new docker-tls credentials installed');
287-
next();
288240
}
241+
242+
db.save();
243+
log('[ok] new docker-tls credentials installed');
289244
};
290245

291246
// Verify OAuth2 client access to a Janitor instance (for cluster hosts).
292-
exports.verifyJanitorOAuth2Access = function (next) {
247+
exports.verifyJanitorOAuth2Access = async function () {
293248
const parameters = {
294249
provider: 'janitor',
295250
path: '/api/hosts/' + hostname,
296251
serviceRequest: true
297252
};
298253

299-
oauth2.request(parameters).then(({ body, response }) => {
300-
log('[ok] verified janitor-oauth2 access');
301-
next();
302-
}).catch(error => {
303-
log('[fail] janitor-oauth2 access problem', error);
304-
});
254+
await oauth2.request(parameters);
255+
log('[ok] verified janitor-oauth2 access');
305256
};
306257

307258
// Provide our Docker TLS client certificates to the Janitor instance.
308-
exports.registerDockerClient = function (next) {
309-
const { ca, client } = db.get('tls');
259+
exports.registerDockerClient = async function () {
260+
const {ca, client} = db.get('tls');
310261
const parameters = {
311262
provider: 'janitor',
312263
path: '/api/hosts/' + hostname,
@@ -320,10 +271,6 @@ exports.registerDockerClient = function (next) {
320271
serviceRequest: true
321272
};
322273

323-
oauth2.request(parameters).then(({ body, response }) => {
324-
log('[ok] registered docker-tls credentials');
325-
next();
326-
}).catch(error => {
327-
log('[fail] unable to register docker-tls credentials:', error);
328-
});
274+
await oauth2.request(parameters);
275+
log('[ok] registered docker-tls credentials');
329276
};

0 commit comments

Comments
 (0)