Skip to content

Commit db5aa4d

Browse files
committed
project worker now uses zip submissions with yml
1 parent 6776c78 commit db5aa4d

File tree

10 files changed

+137
-69
lines changed

10 files changed

+137
-69
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM node:8-alpine
22

33
RUN apk add --no-cache docker
4-
RUN apk add build-base diffutils git
4+
RUN apk add build-base diffutils git bash rsync
55

66
WORKDIR /usr/src/judge-taskmaster
77

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"axios": "^0.19.2",
2424
"newrelic": "^6.5.0",
2525
"raven": "^2.6.4",
26-
"shelljs": "^0.8.1"
26+
"shelljs": "^0.8.1",
27+
"yaml": "^1.10.0"
2728
},
2829
"nyc": {
2930
"extension": [

src/tasks/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function execute (job: Job) {
3737
// Get result
3838
const result = await scenario.result(currentJobDir, job)
3939

40-
rm('-rf', currentJobDir)
40+
// rm('-rf', currentJobDir)
4141

4242
return result
4343
}

src/tasks/jobs/project.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import {Job, JobConstructorOpts} from '../job';
22

33
interface ProjectConstructorOpts extends JobConstructorOpts {
44
problem: string,
5-
lockedFiles: Array<string>
5+
config: string
66
}
77
export class ProjectJob extends Job {
88
problem: string
9-
lockedFiles: Array<string>
9+
config: string
1010

11-
constructor({ id, source, lang, timelimit, scenario, problem, lockedFiles}: ProjectConstructorOpts) {
11+
constructor({ id, source, lang, timelimit, scenario, problem, config}: ProjectConstructorOpts) {
1212
super({ id, source, lang, timelimit, scenario})
1313
this.problem = problem
14-
this.lockedFiles = lockedFiles
14+
this.config = config
1515
}
1616
}

src/tasks/scenarios/project.ts

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import config = require('../../../config.js')
2-
import { cat, exec, mkdir, rm} from 'shelljs'
2+
import * as fs from 'fs'
3+
import * as YAML from 'yaml'
4+
import { cat, exec, mkdir, cp, ls } from 'shelljs'
5+
import { execSync } from 'child_process'
36
import { ProjectJob } from '../jobs/project'
47
import { ProjectResult } from 'types/result'
58
import * as path from 'path'
@@ -8,15 +11,25 @@ import { download } from 'utils/request'
811

912
export default class ProjectScenario extends Scenario {
1013
async setup(currentJobDir: string, job: ProjectJob) {
11-
const problemBundlePath = path.join(currentJobDir, 'problem.git')
12-
const solutionBundlePath = path.join(currentJobDir, 'solution.git')
14+
const projectPath = path.join(currentJobDir, 'project.zip')
15+
const solutionPath = path.join(currentJobDir, 'solution.zip')
16+
const config = YAML.parse(job.config)
17+
1318
await Promise.all([
14-
download(job.problem, problemBundlePath),
15-
download(job.source, solutionBundlePath)
19+
download(job.problem, projectPath),
20+
download(job.source, solutionPath)
1621
])
1722

18-
exec(`git clone ${currentJobDir}/problem.git ${currentJobDir}/problem`)
19-
exec(`git clone ${currentJobDir}/solution.git ${currentJobDir}/solution`)
23+
exec(`mkdir -p ${currentJobDir}/project && unzip -d ${currentJobDir}/project ${projectPath}`)
24+
exec(`mkdir -p ${currentJobDir}/solution && unzip -d ${currentJobDir}/solution ${solutionPath}`)
25+
26+
config.project['allowed-folders'].map(glob => {
27+
execSync(`shopt -s globstar && rsync -R ${glob} ${currentJobDir}/project`, {
28+
cwd: `${currentJobDir}/solution`,
29+
shell: 'bash'
30+
})
31+
})
32+
fs.writeFileSync(`${currentJobDir}/project.yml`, job.config)
2033
}
2134

2235
run(currentJobDir: string, job: ProjectJob) {
@@ -27,31 +40,47 @@ export default class ProjectScenario extends Scenario {
2740
--rm \\
2841
-v "${currentJobDir}":/usr/src/runbox \\
2942
-w /usr/src/runbox codingblocks/project-worker-"${job.lang}" \\
30-
/bin/judge.py -t ${job.timelimit || 5} -l ${job.lockedFiles.join(' ')}
43+
/bin/judge.py -t ${job.timelimit || 20}
3144
`);
3245
}
3346

3447
async result(currentJobDir: string, job: ProjectJob): Promise<ProjectResult> {
35-
const result_code = cat(path.join(currentJobDir, 'result.code')).toString() || '1'
36-
const result_time = cat(path.join(currentJobDir, 'result.time').toString())
37-
38-
const result_stderr = cat(path.join(currentJobDir, 'result.stderr')).toString()
39-
const build_stderr = cat(path.join(currentJobDir, 'build.stderr')).toString()
40-
const run_stderr = cat(path.join(currentJobDir, 'run.stderr')).toString()
48+
const setup_stdout = cat(path.join(currentJobDir, 'setup.stdout')).toString()
49+
const setup_stderr = cat(path.join(currentJobDir, 'setup.stderr')).toString()
50+
51+
if (setup_stderr) {
52+
return {
53+
id: job.id,
54+
scenario: 'project',
55+
stderr: (new Buffer(setup_stderr)).toString('base64'),
56+
testcases: []
57+
}
58+
}
59+
60+
const testcases = ls(path.join(currentJobDir, 'result')).map(index => {
61+
const currentTestcasePath = path.join(currentJobDir, 'result', index)
62+
63+
const stderr = cat(path.join(currentTestcasePath, 'run.stderr')).toString()
64+
const time = cat(path.join(currentTestcasePath, 'run.time')).toString().trim()
65+
const code = cat(path.join(currentTestcasePath, 'run.code')).toString().trim()
66+
const stdout = cat(path.join(currentTestcasePath, 'run.stdout')).toString()
4167

42-
const stdout = cat(path.join(currentJobDir, 'run.stdout')).toString()
43-
const stderr = result_stderr || build_stderr || run_stderr
68+
const score = +code === 0 ? 100 : 0
4469

45-
const score = +result_code === 0 ? 100 : 0
70+
return {
71+
id: +index,
72+
time,
73+
stdout: (new Buffer(stdout)).toString('base64'),
74+
stderr: (new Buffer(stderr)).toString('base64'),
75+
score
76+
}
77+
})
4678

4779
return {
4880
id: job.id,
4981
scenario: 'project',
50-
stderr: (new Buffer(stderr)).toString('base64'),
51-
stdout: (new Buffer(stdout)).toString('base64'),
52-
code: +result_code,
53-
time: +result_time,
54-
score
82+
stderr: (new Buffer(setup_stderr)).toString('base64'),
83+
testcases
5584
}
5685
}
5786
}

src/types/result.d.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ export interface TestcaseResult {
1111
result: string
1212
}
1313

14+
export interface ProjectTestcaseResult {
15+
id: number,
16+
score: number,
17+
time: string,
18+
stdout: string,
19+
stderr: string
20+
}
21+
1422
export interface RunResult extends Result {
1523
stdout: string,
1624
time: number,
@@ -22,8 +30,5 @@ export interface SubmissionResult extends Result {
2230
}
2331

2432
export interface ProjectResult extends Result {
25-
stdout: string,
26-
code: number,
27-
time: number,
28-
score: number
33+
testcases: Array<ProjectTestcaseResult>
2934
}

src/utils/request.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
11
import axios from 'axios';
22
import * as fs from 'fs';
33

4-
export const download = (url: string, dest: string) => {
4+
export const download = async (url: string, dest: string) => {
55
const writer = fs.createWriteStream(dest);
66

7-
return axios({
7+
const response = await axios({
88
method: 'get',
99
url: url,
1010
responseType: 'stream'
11-
}).then(function (response) {
12-
return new Promise((resolve, reject) => {
13-
response.data.pipe(writer);
14-
let error = null;
15-
writer.on('error', err => {
16-
error = err;
17-
writer.close();
18-
reject(err);
19-
});
20-
writer.on('close', () => {
21-
if (!error) {
22-
resolve(true);
23-
}
24-
});
25-
});
26-
});
11+
})
12+
13+
response.data.pipe(writer);
14+
15+
return new Promise((resolve, reject) => {
16+
writer.on('error', err => {
17+
console.log(err)
18+
writer.close();
19+
reject(err);
20+
})
21+
writer.on('close', () => {
22+
resolve();
23+
})
24+
})
2725
}
2826

test/project/project.nodejs.spec.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,32 @@ import { execute } from '../../src/tasks/'
22
import { expect } from 'chai'
33
import { ProjectJob } from "../../src/tasks/jobs/project"
44

5-
describe('submit - nodejs', () => {
5+
describe('project - nodejs', () => {
66
it('nodejs project submits correctly', async () => {
77
const result = await execute(new ProjectJob({
88
id: 4,
99
lang: 'nodejs',
10-
source: 'http://127.0.0.1:8000/solution.git',
11-
problem: 'http://127.0.0.1:8000/problem.git',
12-
lockedFiles: ['package.json', 'yarn.lock', 'test'],
13-
scenario: 'project'
10+
source: 'https://minio.cb.lk/public/sample-solution.zip',
11+
problem: 'https://minio.cb.lk/public/problem.zip',
12+
config: `
13+
project:
14+
allowed-folders:
15+
- src/**/*.js
16+
before-test:
17+
- yarn install
18+
- yarn build
19+
testcases:
20+
- yarn test
21+
`,
22+
scenario: 'project',
23+
timelimit: 60
1424
}))
1525

26+
console.log(result)
27+
1628
// assertions
17-
expect(result.stderr).to.be.equal('')
18-
expect(result.stdout).to.be.not.equal('')
19-
expect(result.score).to.be.equal(100)
29+
expect(1).to.be.equal(1)
30+
// expect(submitResult.testcases[0].result).to.eq('Success')
2031
})
2132
})
2233

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
1+
import { mkdir } from 'shelljs'
12
import { expect } from 'chai'
2-
import { ProjectJob } from '../../src/tasks/jobs/project';
3-
import ProjectScenarion from '../../src/tasks/scenarios/project'
3+
import config = require('../../config.js')
4+
import * as path from 'path'
5+
import { ProjectJob } from '../../src/tasks/jobs/project'
6+
import ProjectScenario from '../../src/tasks/scenarios/project'
47

58
describe('Project Scenario', () => {
69
it('should setup', async () => {
7-
// const job: ProjectJob = {
8-
// id: 1,
9-
// source: '',
10-
// lang: 'nodejs',
11-
// timelimit: 20,
12-
// scenario: 'problem',
13-
// problem: ''
14-
// }
10+
const job: ProjectJob = new ProjectJob({
11+
id: 4,
12+
lang: 'nodejs',
13+
source: 'https://minio.cb.lk/public/sample-solution.zip',
14+
problem: 'https://minio.cb.lk/public/problem.zip',
15+
config: `
16+
project:
17+
allowed-folders:
18+
- src/**/*.js
19+
before-test:
20+
- yarn install
21+
- yarn build
22+
testcases:
23+
- yarn test
24+
`,
25+
scenario: 'project'
26+
})
27+
28+
const currentJobDir = path.join(config.RUNBOX.DIR, job.id.toString())
29+
mkdir('-p', currentJobDir)
30+
const scenario = new ProjectScenario()
31+
await scenario.setup(currentJobDir, job)
32+
33+
expect(1).to.be.equal(1)
1534
})
1635
})

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,11 @@ yallist@^2.1.2:
18081808
version "2.1.2"
18091809
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
18101810

1811+
yaml@^1.10.0:
1812+
version "1.10.0"
1813+
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
1814+
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
1815+
18111816
yargs-parser@^10.1.0:
18121817
version "10.1.0"
18131818
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"

0 commit comments

Comments
 (0)