Skip to content

Commit 5541ca5

Browse files
feat: v0.1.9 (#33)
* fix: -f flag added to npm clean script, prevents failure if ./dist does not exist * feat: v0.1.8 * WIP: added dockerfiles for the remaining langs (#32) * feat: accepting only suported languages * feat: added dockerfiles for the remaining langs * fix: trimming outputs before comparing * fix: error handling got worker.build() * feat: better error handling, closes #18 and #14 * feat: added log for error * feat: v0.1.9 Co-authored-by: Rahil Kabani <[email protected]>
1 parent 9bbae0b commit 5541ca5

23 files changed

+93
-67
lines changed

examples/master.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
/* eslint-disable no-console */
21
import CodeExecutor from '../src/CodeExecutor';
32
import logger from '../src/utils/logger';
43

54
const codeExecutor = new CodeExecutor('myExecutor', 'redis://127.0.0.1:6379');
65

7-
/**
8-
* base64: true is also an option if input,
9-
* output and code are encoded in base64,
10-
* default is false
11-
* */
12-
136
const pythonCode = `
147
import time
158
time.sleep(1)
169
print('hello')
1710
`;
1811

12+
const bashCode = `
13+
echo hello
14+
`;
15+
1916
const inputs = [{
2017
language: 'Python',
2118
code: pythonCode,
@@ -29,7 +26,7 @@ const inputs = [{
2926
},
3027
{
3128
language: 'Bash',
32-
code: 'echo hello',
29+
code: bashCode,
3330
testCases: [
3431
{
3532
input: '',
@@ -43,7 +40,7 @@ async function main() {
4340
const results = await Promise.all(
4441
inputs.map((input) => codeExecutor.runCode(input)),
4542
);
46-
logger.info(results);
43+
logger.info(JSON.stringify(results));
4744
codeExecutor.stop();
4845
}
4946

examples/worker.ts

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,11 @@
1-
import { Worker, languages } from '../src/CodeExecutor';
2-
import logger from '../src/utils/logger';
1+
import { Worker } from '../src/CodeExecutor';
32

4-
/**
5-
* name, redis, options
6-
*
7-
* Options:
8-
*
9-
* folderPath ( path to mount, default /tmp/code-exec ),
10-
* memory (in MB, default 0, ie no limit),
11-
* CPUs (no. of CPUs, default 0.5),
12-
*/
133
const worker = new Worker('myExecutor', 'redis://127.0.0.1:6379');
144

155
async function main() {
16-
logger.info(languages);
17-
18-
/* array of languages is optional argument */
19-
await worker.build(['Python', 'Bash']);
6+
await worker.build();
207

218
worker.start();
22-
23-
worker.pause();
24-
25-
worker.resume();
26-
27-
// worker.stop();
289
}
2910

3011
main();

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "code-executor",
3-
"version": "0.1.8",
3+
"version": "0.1.9",
44
"description": "A CLI/library to execute code against test cases in various languages and obtain relevant results.",
55
"main": "dist/src/CodeExecutor.js",
66
"keywords": [

src/Builder.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,20 @@ export default class Builder {
2626
t: `${lang.toLowerCase()}-runner`,
2727
}));
2828
} else {
29-
logger.error(`${lang} is not supported`);
29+
streams.push(Promise.reject(new Error(`${lang} is not supported`)));
3030
}
3131
});
3232

33-
const progress: Promise<object>[] = [];
33+
let resolvedStreams;
34+
try {
35+
resolvedStreams = await Promise.all(streams);
36+
} catch (e) {
37+
logger.error(e);
38+
return Promise.reject(e);
39+
}
3440

35-
(await Promise.all(streams)).forEach((stream) => {
41+
const progress: Promise<object>[] = [];
42+
(resolvedStreams).forEach((stream) => {
3643
stream.on('data', (chunk) => {
3744
logger.debug(chunk);
3845
});
@@ -50,8 +57,13 @@ export default class Builder {
5057
});
5158
}));
5259
});
53-
54-
await Promise.all(progress);
60+
try {
61+
await Promise.all(progress);
62+
} catch (e) {
63+
logger.error(e);
64+
return Promise.reject(e);
65+
}
5566
logger.info('Built containers successfully');
67+
return null;
5668
}
5769
}

src/CodeExecutor.ts

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Bull from 'bull';
22
import { v4 as uuid } from 'uuid';
33

4-
import { CodeParams, Result } from './models/models';
4+
import { CodeParams } from './models/models';
55
import logger from './utils/logger';
66
import { extension } from './utils/findExtension';
77

@@ -10,35 +10,21 @@ const languages = Object.keys(extension);
1010
export default class CodeExecutor {
1111
private queue: Bull.Queue;
1212

13-
private jobs: Map<string, { resolve: Function, reject: Function }>;
14-
1513
constructor(name: string, redis: string) {
1614
this.queue = new Bull(name, redis);
17-
18-
this.jobs = new Map();
19-
20-
this.queue.on('global:completed', (_job: Bull.Job, result: string) => {
21-
const { id } = <Result>JSON.parse(result);
22-
23-
logger.debug(`Running on complete for id: ${id}`);
24-
25-
const currentJob = this.jobs.get(id);
26-
if (currentJob) {
27-
currentJob.resolve(result);
28-
this.jobs.delete(id);
29-
}
30-
});
3115
}
3216

3317
async runCode(codeOptions: CodeParams): Promise<void> {
18+
if (!languages.includes(codeOptions.language)) {
19+
return Promise.reject(new Error('Language not supported!'));
20+
}
3421
const id = uuid();
3522
const codeObject = { ...codeOptions, id };
3623
logger.info(`Running code with id: ${id}`);
3724

38-
return new Promise((resolve, reject) => {
39-
this.jobs.set(id, { resolve, reject });
40-
this.queue.add(codeObject);
41-
});
25+
const job = await this.queue.add(codeObject);
26+
27+
return job.finished();
4228
}
4329

4430
stop() {

src/Runner.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export default class Runner {
4848
const promisesToKeep: Array<Promise<Array<object>>> = [];
4949
for (let i = 0; i < Paths.length; i += 1) {
5050
promisesToKeep.push(this.docker.run(tag, ['bash', '/start.sh', `${i}`, `${timeout}`], null, {
51+
User: 'runner',
5152
HostConfig: {
5253
CpuPeriod: 100000,
5354
CpuQuota: CPUs * 1000000,
@@ -64,7 +65,11 @@ export default class Runner {
6465
}
6566
logger.info(`Starting process ${id}`);
6667
const t0 = performance.now();
67-
await Promise.all(promisesToKeep);
68+
try {
69+
await Promise.all(promisesToKeep);
70+
} catch (e) {
71+
return Promise.reject(e);
72+
}
6873
const t1 = performance.now();
6974
logger.info(`Process ${id} completed in ${(t1 - t0) / 1000} seconds`);
7075

@@ -88,7 +93,7 @@ export default class Runner {
8893
if (exitCode === 124) {
8994
remarks = 'Time limit exceeded';
9095
} else if (exitCode === 0) {
91-
remarks = expectedOutput === obtainedOutput ? 'Pass' : 'Fail';
96+
remarks = expectedOutput.trim() === obtainedOutput.trim() ? 'Pass' : 'Fail';
9297
} else {
9398
remarks = 'Error';
9499
}

src/Worker.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,26 @@ export default class Worker {
5555
return result;
5656
}
5757

58-
async build(langs?: Array<string>) {
59-
await this.builder.build(langs);
58+
async build(langs?: Array<string>): Promise<void> {
59+
try {
60+
await this.builder.build(langs);
61+
return null;
62+
} catch (e) {
63+
return Promise.reject(e);
64+
}
6065
}
6166

6267
start() {
6368
this.queue.process(async (job, done) => {
6469
logger.info(`Received: ${job.data.id}`);
65-
const result = await this.work(job.data);
66-
67-
logger.debug(JSON.stringify(result));
68-
done(null, result);
70+
try {
71+
const result = await this.work(job.data);
72+
done(null, result);
73+
logger.debug(JSON.stringify(result));
74+
} catch (e) {
75+
done(e);
76+
logger.error(e);
77+
}
6978
});
7079
}
7180

src/langs/Bash/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM bash:5.0.18
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/langs/Brainfuck/Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM esolang/brainfuck-esotope:latest
2+
RUN adduser --disabled-password --gecos "" runner
3+
COPY start.sh start.sh
4+
WORKDIR /app

src/langs/Brainfuck/start.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
i=$1
2+
date +%s%N > /app/time$i.txt
3+
timeout $2 bash -c "(cat /app/in$i.txt | brainfuck-esotope /app/Main.bf) 2> /app/error$i.txt 1> /app/output$i.txt"
4+
echo $? > /app/exitCode$i.txt
5+
date +%s%N >> /app/time$i.txt

src/langs/C/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM gcc:4.9
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/langs/Golfscript/Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM esolang/golfscript:latest
2+
RUN adduser --disabled-password --gecos "" runner
3+
COPY start.sh start.sh
4+
WORKDIR /app

src/langs/Golfscript/start.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
i=$1
2+
date +%s%N > /app/time$i.txt
3+
timeout $2 bash -c "(cat /app/in$i.txt | golfscript /app/Main.gs) 2> /app/error$i.txt 1> /app/output$i.txt"
4+
echo $? > /app/exitCode$i.txt
5+
date +%s%N >> /app/time$i.txt

src/langs/Java/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM java:8
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/langs/Javascript/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM node:10-alpine
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/langs/O5AB1E/Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM esolang/05ab1e:latest
2+
RUN adduser --disabled-password --gecos "" runner
3+
COPY start.sh start.sh
4+
WORKDIR /app

src/langs/O5AB1E/start.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
i=$1
2+
date +%s%N > /app/time$i.txt
3+
timeout $2 bash -c "(cat /app/in$i.txt | 05ab1e /app/Main.abe) 2> /app/error$i.txt 1> /app/output$i.txt"
4+
echo $? > /app/exitCode$i.txt
5+
date +%s%N >> /app/time$i.txt

src/langs/Perl/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM perl:5.20
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/langs/Python/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM python:3.8-slim
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/langs/Ruby/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM ruby:2
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/langs/Rust/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM rust:1.31
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/langs/Swift/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
FROM swift
1+
FROM swift:latest
2+
RUN adduser --disabled-password --gecos "" runner
23
COPY start.sh start.sh
34
WORKDIR /app

src/utils/findExtension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default function findExtension(language: string) {
55
let fileExtension = '';
66
Object.entries(extension).forEach((entry) => {
77
const [key, value] = entry;
8-
if (key.toLowerCase() === language.toLowerCase()) {
8+
if (key === language) {
99
fileExtension = value;
1010
}
1111
});

0 commit comments

Comments
 (0)