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 Docker 18+
0. Install Node 10+ 0. Install Node 10+
0. Run `npm i` 0. Run `npm i`
0. Fill out `config.json` 0. Fill out `config.json` as described in the configuration section below
0. Run `node .`
## Configuration
### Bot
- `owner` - The owner(s) of the bot. - `owner` - The owner(s) of the bot.
Use an array for multiple owners. Use an array for multiple owners.
- `token` - The bot token. - `token` - The bot token.
- `prefix` - The prefix for commands. - `prefix` - The prefix for commands.
- `codePrefix` - The prefix for code evaluation. - `codePrefix` - The prefix for code evaluation.
### Setup
- `languages` Languages to use. - `languages` Languages to use.
The language names here are different from the user-facing ones. The language names here are different from the user-facing ones.
Check the filenames in `src/languages/` for the language names. Check the filenames in `src/languages/` for the language names.
Change to `null` to enable all languages. 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. - `memory` Max memory usage of a container.
- `cpu` Max CPU usage of a container. - `cpu` Max CPU usage of a container.
- `timeout` Time limit for code in milliseconds. - `timeout` Time limit for code in milliseconds.
- `prepare` Whether to start containers on setup. - `concurrent` Number of code evaluations than can run at a time per container.
Setting to true will speed up the first eval, but that language might not be used. The more that can run, the more resources a container would need.
- `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. Run `node .`

View file

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

View file

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