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:
parent
6460615923
commit
de45491586
3 changed files with 64 additions and 27 deletions
55
README.md
55
README.md
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue