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
45
README.md
45
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 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
|
||||||
- `owner` - The owner(s) of the bot.
|
0. Run `node .`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### 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.
|
||||||
- `languages` Languages to use.
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
- `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.
|
||||||
- `memory` Max memory usage of a container.
|
- `prepare` Whether to start containers on setup.
|
||||||
- `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.
|
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.
|
- `parallel` Whether to build images in parallel.
|
||||||
|
Will also setup containers in parallel if `prepare` is set.
|
||||||
Faster, but will take more resources.
|
Faster, but will take more resources.
|
||||||
- `concurrent` Number of code evaluations per language than can run at a time.
|
- `cleanup` Interval in minutes to occasionally kill all containers.
|
||||||
0. Run `node .`
|
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",
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue