-
-
Notifications
You must be signed in to change notification settings - Fork 25
/
cht-docker-utils.js
129 lines (109 loc) · 4.32 KB
/
cht-docker-utils.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
const path = require('path');
const fs = require('fs');
const https = require('https');
const { spawn } = require('child_process');
const fse = require('fs-extra');
const request = require('request-promise-native');
const log = require('../../src/lib/log');
const DEFAULT_PROJECT_NAME = 'cht_conf_e2e_tests';
const dockerHelperDirectory = path.resolve(__dirname, '.cht-docker-helper');
const dockerHelperScript = path.resolve(dockerHelperDirectory, './cht-docker-compose.sh');
const downloadDockerHelperScript = () => new Promise((resolve, reject) => {
const file = fs.createWriteStream(dockerHelperScript, { mode: 0o755 });
https
.get('https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper-4.x/cht-docker-compose.sh', (response) => {
response.pipe(file);
file.on('finish', () => file.close(resolve));
file.on('error', () => file.close(reject));
})
.on('error', () => {
fs.unlinkSync(file.path);
file.close(() => reject('Failed to download CHT Docker Helper script "cht-docker-compose.sh"'));
});
});
const ensureScriptExists = async () => {
if (!fs.existsSync(dockerHelperDirectory)) {
await fs.promises.mkdir(dockerHelperDirectory);
}
if (!fs.existsSync(dockerHelperScript)) {
await downloadDockerHelperScript();
}
};
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const getProjectConfig = async (projectName) => {
const configFilePath = path.resolve(dockerHelperDirectory, `${projectName}.env`);
if (!fs.existsSync(configFilePath)) {
throw new Error(`Unexpected error: config file not found at ${configFilePath}`);
}
const configFile = await fs.promises.readFile(configFilePath, 'utf8');
return Object.fromEntries(
configFile.toString()
.split('\n')
.map(line => line.split('='))
.filter(entry => entry.length === 2),
);
};
const getProjectUrl = async (projectName = DEFAULT_PROJECT_NAME) => {
const config = await getProjectConfig(projectName);
const { COUCHDB_USER, COUCHDB_PASSWORD, NGINX_HTTPS_PORT } = config;
return `https://${COUCHDB_USER}:${COUCHDB_PASSWORD}@127-0-0-1.local-ip.medicmobile.org:${NGINX_HTTPS_PORT}`;
};
const isProjectReady = async (projectName, attempt = 1) => {
log.info(`Checking if CHT is ready, attempt ${attempt}.`);
const url = await getProjectUrl(projectName);
await request({ uri: `${url}/api/v2/monitoring`, json: true })
.catch(async (error) => {
if (
error.error.code !== 'DEPTH_ZERO_SELF_SIGNED_CERT' ||
![502, 503].includes(error.statusCode)
) {
// unexpected error, log it to keep a trace,
// but we'll keep retrying until the instance is up, or we hit the timeout limit
log.trace(error);
}
await sleep(1000);
return isProjectReady(projectName, attempt + 1);
});
};
const startProject = (projectName) => new Promise((resolve, reject) => {
log.info(`Starting CHT instance "${projectName}"`);
// stdio: 'pipe' to answer the prompts to initialize a project by writing to stdin
const childProcess = spawn(dockerHelperScript, { stdio: 'pipe', cwd: dockerHelperDirectory });
childProcess.on('error', reject);
childProcess.on('close', async () => {
await isProjectReady(projectName);
resolve();
});
childProcess.stdin.write('y\n');
childProcess.stdin.write('y\n');
childProcess.stdin.write(`${projectName}\n`);
});
const destroyProject = (projectName) => new Promise((resolve, reject) => {
// stdio: 'inherit' to see the script's logs and understand why it requests elevated permissions when cleaning up project files
const childProcess = spawn(dockerHelperScript, [`${projectName}.env`, 'destroy'], {
stdio: 'inherit',
cwd: dockerHelperDirectory,
});
childProcess.on('error', reject);
childProcess.on('close', resolve);
});
const spinUpCht = async (projectName = DEFAULT_PROJECT_NAME) => {
await ensureScriptExists();
await startProject(projectName);
};
const tearDownCht = async (projectName = DEFAULT_PROJECT_NAME) => {
if (!fs.existsSync(dockerHelperDirectory)) {
return;
}
if (fs.existsSync(path.resolve(dockerHelperDirectory, `${projectName}.env`))) {
await ensureScriptExists();
await destroyProject(projectName);
}
fse.removeSync(dockerHelperDirectory);
};
module.exports = {
DEFAULT_PROJECT_NAME,
getProjectUrl,
spinUpCht,
tearDownCht,
};