Keep containers running instead of making new ones

This commit is contained in:
1computer1 2019-05-14 19:50:21 -04:00
parent b45e7c69e3
commit 389b00ba15
3 changed files with 77 additions and 23 deletions

18
src/commands/exit.js Normal file
View file

@ -0,0 +1,18 @@
const { Command } = require('discord-akairo');
class ExitCommand extends Command {
constructor() {
super('exit', {
aliases: ['exit'],
ownerOnly: true,
clientPermissions: ['SEND_MESSAGES']
});
}
async exec() {
await this.client.languageHandler.cleanup();
process.exit();
}
}
module.exports = ExitCommand;

View file

@ -27,7 +27,7 @@ class MessageInvalidListener extends Listener {
} }
let errored = false; let errored = false;
const result = await this.client.languageHandler.evalCode(message, parse) const result = await this.client.languageHandler.evalCode(parse)
.catch(e => { .catch(e => {
errored = true; errored = true;
return e.message; return e.message;

View file

@ -23,6 +23,7 @@ class LanguageHandler extends AkairoHandler {
}); });
this.aliases = new Collection(); this.aliases = new Collection();
this.containers = new Collection();
} }
register(language, filepath) { register(language, filepath) {
@ -59,30 +60,50 @@ class LanguageHandler extends AkairoHandler {
})); }));
} }
evalCode(message, { language, code, options }) { async setupContainer(id) {
return new Promise((resolve, reject) => { if (this.containers.has(id)) {
const name = `comp_iler-${message.id}-${Date.now()}`; return this.containers.get(id);
const { id = language.id, env = {} } = language.runWith(options); }
const name = `comp_iler-${id}-${Date.now()}`;
const proc = childProcess.spawn('docker', [ const proc = childProcess.spawn('docker', [
'run', '--rm', `--name=${name}`, '-u1000', '-w/tmp/', 'run', '--rm', `--name=${name}`, '-u1000', '-w/tmp/', '-t', '-d',
'--net=none', `--cpus=${this.client.config.cpus}`, `-m=${this.client.config.memory}`, '--net=none', `--cpus=${this.client.config.cpus}`, `-m=${this.client.config.memory}`,
...Object.entries(env).map(([k, v]) => `-e${k}=${v}`), `1computer1/comp_iler:${id}`
`1computer1/comp_iler:${id}`,
'/bin/sh', '/var/run/run.sh', code
]); ]);
setTimeout(() => {
try { try {
if (process.platform === 'win32') { await this.handleSpawn(proc);
childProcess.execSync(`docker kill --signal=9 ${name} >nul 2>nul`); this.containers.set(id, { name });
} else { return this.containers.get(id);
childProcess.execSync(`docker kill --signal=9 ${name} >/dev/null 2>/dev/null`); } catch (err) {
throw err;
}
} }
reject(new Error('Timed out')); async evalCode({ language, code, options }) {
} catch (e) { const { id = language.id, env = {} } = language.runWith(options);
reject(e); const { name } = await this.setupContainer(id);
const proc = childProcess.spawn('docker', [
'exec',
...Object.entries(env).map(([k, v]) => `-e${k}=${v}`),
name, '/bin/sh', '/var/run/run.sh', code
]);
try {
const result = await this.handleSpawn(proc);
return result;
} catch (err) {
this.images.delete(id);
await this.kill(name);
throw err;
} }
}
handleSpawn(proc) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Timed out'));
}, this.client.config.timeout); }, this.client.config.timeout);
let data = ''; let data = '';
@ -108,6 +129,21 @@ class LanguageHandler extends AkairoHandler {
}); });
}); });
} }
kill(name) {
let cmd;
if (process.platform === 'win32') {
cmd = `docker kill --signal=9 ${name} >nul 2>nul`;
} else {
cmd = `docker kill --signal=9 ${name} >/dev/null 2>/dev/null`;
}
return util.promisify(childProcess.exec)(cmd);
}
cleanup() {
return Promise.all(this.images.map(({ name }) => this.kill(name)));
}
} }
module.exports = LanguageHandler; module.exports = LanguageHandler;