-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathDockerSandbox.js
executable file
·200 lines (169 loc) · 8.17 KB
/
DockerSandbox.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/**
* @Constructor
* @variable DockerSandbox
* @description This constructor stores all the arguments needed to prepare and execute a Docker Sandbox
* @param {Number} timeout_value: The Time_out limit for code execution in Docker
* @param {String} path: The current working directory where the current API folder is kept
* @param {String} folder: The name of the folder that would be mounted/shared with Docker container, this will be concatenated with path
* @param {String} vm_name: The TAG of the Docker VM that we wish to execute
* @param {String} compiler_name: The compiler/interpretor to use for carrying out the translation
* @param {String} file_name: The file_name to which source code will be written
* @param {String} code: The actual code
* @param {String} output_command: Used in case of compilers only, to execute the object code, send " " in case of interpretors
*/
var Problems = require('./app/models/problems')
var DockerSandbox = function (timeout_value, path, folder, vm_name, compiler_name, file_name, code, output_command, languageName, e_arguments, stdin_data, problemid, currentuser) {
this.timeout_value = timeout_value
this.path = path
this.folder = folder
this.vm_name = vm_name
this.compiler_name = compiler_name
this.file_name = file_name
this.code = code
this.problemid = problemid
this.output_command = output_command
this.langName = languageName
this.extra_arguments = e_arguments
this.stdin_data = stdin_data
this.currentuser = currentuser
}
/**
* @function
* @name DockerSandbox.run
* @description Function that first prepares the Docker environment and then executes the Docker sandbox
* @param {Function pointer} success ?????
*/
DockerSandbox.prototype.run = function (success) {
var sandbox = this
this.prepare(function () {
sandbox.execute(success)
})
}
/*
* @function
* @name DockerSandbox.prepare
* @description Function that creates a directory with the folder name already provided through constructor
* and then copies contents of folder named Payload to the created folder, this newly created folder will be mounted
* on the Docker Container. A file with the name specified in file_name variable of this class is created and all the
* code written in 'code' variable of this class is copied into this file.
* Summary: This function produces a folder that contains the source file and 2 scripts, this folder is mounted to our
* Docker container when we run it.
* @param {Function pointer} success ?????
*/
DockerSandbox.prototype.prepare = function (success) {
var exec = require('child_process').exec
var fs = require('fs')
var sandbox = this
exec('mkdir ' + this.path + this.folder + ' && cp ' + this.path + '/Payload/* ' + this.path + this.folder + '&& chmod 777 ' + this.path + this.folder, function (st) {
fs.writeFile(sandbox.path + sandbox.folder + '/' + sandbox.file_name, sandbox.code, function (err) {
if (err) {
console.log(err)
}else {
console.log(sandbox.langName + ' file was saved!')
exec("chmod 777 '" + sandbox.path + sandbox.folder + '/' + sandbox.file_name + "'")
fs.writeFile(sandbox.path + sandbox.folder + '/inputFile', sandbox.stdin_data, function (err) {
if (err) {
console.log(err)
}else {
console.log('Input file was saved!')
console.log('open')
success()
}
})
}
})
})
}
/*
* @function
* @name DockerSandbox.execute
* @precondition: DockerSandbox.prepare() has successfully completed
* @description: This function takes the newly created folder prepared by DockerSandbox.prepare() and spawns a Docker container
* with the folder mounted inside the container with the name '/usercode/' and calls the script.sh file present in that folder
* to carry out the compilation. The Sandbox is spawned ASYNCHRONOUSLY and is supervised for a timeout limit specified in timeout_limit
* variable in this class. This function keeps checking for the file "Completed" until the file is created by script.sh or the timeout occurs
* In case of timeout an error message is returned back, otherwise the contents of the file (which could be the program output or log of
* compilation error) is returned. In the end the function deletes the temporary folder and exits
*
* Summary: Run the Docker container and execute script.sh inside it. Return the output generated and delete the mounted folder
*
* @param {Function pointer} success ?????
*/
DockerSandbox.prototype.execute = function (success) {
var exec = require('child_process').exec
var mongoose = require('mongoose')
var Solution = require('./app/models/solution')
var Problems = require('./app/models/problems')
var fs = require('fs')
var myC = 0 // variable to enforce the timeout_value
var sandbox = this
console.log('input extracted is' + sandbox.stdin_data)
var probidmodule = require('./views/solve.ejs')
// this statement is what is executed
var st = this.path + 'DockerTimeout.sh ' + this.timeout_value + 's -u mysql -e \'NODE_PATH=/usr/local/lib/node_modules\' -i -t -v "' + this.path + this.folder + '":/usercode ' + this.vm_name + ' /usercode/script.sh ' + this.compiler_name + ' ' + this.file_name + ' ' + this.output_command + ' ' + this.extra_arguments
// log the statement in console
console.log(st)
// execute the Docker, This is done ASYNCHRONOUSLY
exec(st)
console.log('------------------------------')
// Check For File named "completed" after every 1 second
var intid = setInterval(function () {
// Displaying the checking message after 1 second interval, testing purposes only
// console.log("Checking " + sandbox.path+sandbox.folder + ": for completion: " + myC)
myC = myC + 1
fs.readFile(sandbox.path + sandbox.folder + '/completed', 'utf8', function (err, data) {
// if file is not available yet and the file interval is not yet up carry on
if (err && myC < sandbox.timeout_value) {
// console.log(err)
return
}
// if file is found simply display a message and proceed
else if (myC < sandbox.timeout_value) {
console.log('DONE')
// check for possible errors
fs.readFile(sandbox.path + sandbox.folder + '/errors', 'utf8', function (err2, data2) {
if (!data2) data2 = ''
console.log('Error file: ')
console.log(data2)
console.log('Main File')
console.log(data)
var lines = data.toString().split('*-COMPILEBOX::ENDOFOUTPUT-*')
data = lines[0]
data.toString()
data = data.replace(/(\r\n|\n|\r)/gm, '')
console.log('this is' + data)
var exectime = lines[1]
// name is a member of myModule due to the export above
console.log('Time: ')
console.log(exectime)
success(data, exectime, data2)
})
// return the data to the calling functoin
}
// if time is up. Save an error message to the data variable
else {
// Since the time is up, we take the partial output and return it.
fs.readFile(sandbox.path + sandbox.folder + '/logfile.txt', 'utf8', function (err, data) {
if (!data) data = ''
data += '\nExecution Timed Out'
console.log('Timed Out: ' + sandbox.folder + ' ' + sandbox.langName)
fs.readFile(sandbox.path + sandbox.folder + '/errors', 'utf8', function (err2, data2) {
if (!data2) data2 = ''
var lines = data.toString().split('*---*')
data = lines[0]
var time = lines[1]
console.log('Time: ')
console.log(time)
success(data, data2)
})
})
}
// now remove the temporary directory
console.log('ATTEMPTING TO REMOVE: ' + sandbox.folder)
console.log('------------------------------')
exec('rm -r ' + sandbox.folder)
clearInterval(intid)
})
}, 1000)
}
module.exports = DockerSandbox