Expand configuration options

Add cleanup interval option
Enhanced some options to be per-compiler
Fix concurrent option not being used
This commit is contained in:
1computer1 2019-05-20 20:26:28 -04:00
parent 6460615923
commit de45491586
3 changed files with 64 additions and 27 deletions

View file

@ -106,22 +106,41 @@ The container is locked down, so there is no networking, limited memory and CPU
0. Install Docker 18+
0. Install Node 10+
0. Run `npm i`
0. Fill out `config.json`
- `owner` - The owner(s) of the bot.
Use an array for multiple owners.
- `token` - The bot token.
- `prefix` - The prefix for commands.
- `codePrefix` - The prefix for code evaluation.
- `languages` Languages to use.
The language names here are different from the user-facing ones.
Check the filenames in `src/languages/` for the language names.
Change to `null` to enable all languages.
- `memory` Max memory usage of a container.
- `cpu` Max CPU usage of a container.
- `timeout` Time limit for code in milliseconds.
- `prepare` Whether to start containers on setup.
Setting to true will speed up the first eval, but that language might not be used.
- `parallel` Whether to build images and container in parallel.
Faster, but will take more resources.
- `concurrent` Number of code evaluations per language than can run at a time.
0. Fill out `config.json` as described in the configuration section below
0. Run `node .`
## Configuration
### Bot
- `owner` - The owner(s) of the bot.
Use an array for multiple owners.
- `token` - The bot token.
- `prefix` - The prefix for commands.
- `codePrefix` - The prefix for code evaluation.
### Setup
- `languages` Languages to use.
The language names here are different from the user-facing ones.
Check the filenames in `src/languages/` for the language names.
Change to `null` to enable all languages.
- `prepare` Whether to start containers on setup.
Setting to true will speed up the first eval, but that language might not be used.
- `parallel` Whether to build images in parallel.
Will also setup containers in parallel if `prepare` is set.
Faster, but will take more resources.
- `cleanup` Interval in minutes to occasionally kill all containers.
Set to `0` to disable.
### Compilers
For each of these options, you can use either the expected value to set it for all compilers or an object with compiler names to the expected values.
If using an object, you can set the default with the `default` key.
The compiler names are the folder names under `docker/`.
- `memory` Max memory usage of a container.
- `cpu` Max CPU usage of a container.
- `timeout` Time limit for code in milliseconds.
- `concurrent` Number of code evaluations than can run at a time per container.
The more that can run, the more resources a container would need.

View file

@ -8,10 +8,11 @@
"python",
"javascript"
],
"prepare": false,
"parallel": false,
"cleanup": 60,
"memory": "256m",
"cpus": "0.25",
"timeout": 10000,
"prepare": false,
"parallel": false,
"concurrent": 10
}

View file

@ -65,6 +65,10 @@ class LanguageHandler extends AkairoHandler {
await this.buildImage(dockerID);
}
}
if (this.client.config.cleanup > 0) {
setInterval(() => this.cleanup().catch(() => null), this.client.config.cleanup * 60 * 1000);
}
}
async buildImage(dockerID) {
@ -72,7 +76,8 @@ class LanguageHandler extends AkairoHandler {
await util.promisify(childProcess.exec)(`docker build -t "1computer1/comp_iler:${dockerID}" ${folder}`);
// eslint-disable-next-line no-console
console.log(`Built image 1computer1/comp_iler:${dockerID}.`);
this.queues.set(dockerID, new Queue(10));
const concurrent = this.getCompilerConfig(dockerID, 'concurrent', 'number');
this.queues.set(dockerID, new Queue(concurrent));
if (this.client.config.prepare) {
await this.setupContainer(dockerID);
}
@ -83,11 +88,13 @@ class LanguageHandler extends AkairoHandler {
return this.containers.get(dockerID);
}
const cpus = this.getCompilerConfig(dockerID, 'cpus', 'string');
const memory = this.getCompilerConfig(dockerID, 'memory', 'string');
const name = `comp_iler-${dockerID}-${Date.now()}`;
const proc = childProcess.spawn('docker', [
'run', '--rm', `--name=${name}`, '-u1000', '-w/tmp/', '-dt',
'--net=none', `--cpus=${this.client.config.cpus}`,
`-m=${this.client.config.memory}`, `--memory-swap=${this.client.config.memory}`,
'--net=none', `--cpus=${cpus}`,
`-m=${memory}`, `--memory-swap=${memory}`,
`1computer1/comp_iler:${dockerID}`, '/bin/sh'
]);
@ -120,8 +127,9 @@ class LanguageHandler extends AkairoHandler {
name, '/bin/sh', '/var/run/run.sh', code
]);
const timeout = this.getCompilerConfig(dockerID, 'timeout', 'number');
try {
const result = await this.handleSpawn(proc, true);
const result = await this.handleSpawn(proc, timeout);
return result;
} catch (err) {
this.containers.delete(dockerID);
@ -131,12 +139,12 @@ class LanguageHandler extends AkairoHandler {
});
}
handleSpawn(proc, withTimeout = false) {
handleSpawn(proc, timeout = null) {
return new Promise((resolve, reject) => {
if (withTimeout) {
if (timeout !== null) {
setTimeout(() => {
reject(new Error('Timed out'));
}, this.client.config.timeout);
}, timeout);
}
let data = '';
@ -177,6 +185,15 @@ class LanguageHandler extends AkairoHandler {
cleanup() {
return Promise.all(this.containers.map(({ name }) => this.kill(name)));
}
getCompilerConfig(dockerID, key, type) {
const o = this.client.config[key];
return typeof o === type
? o
: o[dockerID] !== null
? o[dockerID]
: o.default;
}
}
module.exports = LanguageHandler;