@@ -14,22 +14,35 @@ import {
1414} from "./types/worker" ;
1515import { containerCPULimit , containerMemLimit , mainClassName } from "./config" ;
1616
17+
18+ const fileFormats : Record < language , fileFormat > = {
19+ python3 : "py" ,
20+ javascript : "js" ,
21+ } ;
22+
1723class JobWorker {
18- constructor ( ) { }
24+ public language : language
25+ public codeContext : CodeContext
26+ public filename : string
27+
28+ constructor ( language : language , codeContext : CodeContext ) {
29+ this . language = language
30+ this . codeContext = codeContext
31+ this . filename = ""
32+ }
33+
34+ containerID : string = ""
35+ cacheFilename : string = ""
1936
20- private _fileFormats : Record < language , fileFormat > = {
21- python3 : "py" ,
22- javascript : "js" ,
23- } ;
2437
2538 /**
2639 * Creates an appropriate docker container
2740 * to execute the target code
2841 */
29- createContainer ( language : language ) : Promise < ContainerInitialization > {
42+ createContainer ( ) : Promise < ContainerInitialization > {
3043 return new Promise ( ( resolve , reject ) => {
31- const initCommand = `docker create ${ containerMemLimit } ${ containerCPULimit } ${ language } ` ;
32- if ( ! ( language in this . _fileFormats ) ) {
44+ const initCommand = `docker create ${ containerMemLimit } ${ containerCPULimit } ${ this . language } ` ;
45+ if ( ! ( this . language in fileFormats ) ) {
3346 return reject ( {
3447 error : true ,
3548 errorMessage : "Invalid container name." ,
@@ -48,6 +61,7 @@ class JobWorker {
4861 errorMessage : stderr ,
4962 } ) ;
5063 } else {
64+ this . containerID = containerID . trim ( )
5165 resolve ( {
5266 error : false ,
5367 containerID : containerID . trim ( ) ,
@@ -61,10 +75,10 @@ class JobWorker {
6175 * Returns a piece of code that executes the class method
6276 * this is language specific, so defined using switch cases
6377 */
64- transformCodeIntoExecutable ( language : language , context : CodeContext ) : string {
65- switch ( language ) {
78+ transformCodeIntoExecutable ( ) : string {
79+ switch ( this . language ) {
6680 case "python3" : {
67- return `\nprint(${ mainClassName } ().${ context . functionName } ())`
81+ return `\nprint(${ mainClassName } ().${ this . codeContext . functionName } ())`
6882 }
6983 default : return ""
7084 }
@@ -73,24 +87,21 @@ class JobWorker {
7387 /**
7488 * writes a code into a temp file
7589 */
76- private async writeFile (
77- language : language ,
78- context : CodeContext
79- ) : Promise < WriteFileStatus > {
90+ private async writeFile ( ) : Promise < WriteFileStatus > {
8091 return new Promise ( ( resolve , reject ) => {
81- const fileName = crypto . randomBytes ( 32 ) . toString ( "hex" ) ;
82- const fileFormat = this . _fileFormats [ language ] ;
92+ const fileFormat : string = fileFormats [ this . language ] ;
93+ this . filename = ` ${ crypto . randomBytes ( 32 ) . toString ( "hex" ) } . ${ fileFormat } `
8394 const filePath = path . join (
8495 __dirname ,
8596 ".." ,
8697 "temp" ,
87- `${ fileName } . ${ fileFormat } `
98+ `${ this . filename } `
8899 ) ;
89100
90101 // appending a line to execute a specific function
91- context . code += this . transformCodeIntoExecutable ( language , context )
102+ this . codeContext . code += this . transformCodeIntoExecutable ( )
92103
93- fs . writeFile ( filePath , context . code , ( error ) => {
104+ fs . writeFile ( filePath , this . codeContext . code , ( error ) => {
94105 if ( error ) {
95106 reject ( error . message ) ;
96107 } else {
@@ -100,37 +111,19 @@ class JobWorker {
100111 } ) ;
101112 }
102113
103- /**
104- * Removes a container with the provided containerID
105- */
106- async removeContainer ( containerID : string ) : Promise < Stdout > {
107- return new Promise ( ( resolve , reject ) => {
108- const removeContainer = `docker rm --force ${ containerID } ` ;
109- child . exec ( removeContainer , ( error , stdout , stderr ) => {
110- if ( error ) {
111- reject ( error . message ) ;
112- } else if ( stderr ) {
113- reject ( stderr ) ;
114- } else {
115- resolve ( stdout ) ;
116- }
117- } ) ;
118- } ) ;
119- }
120114
121115 /**
122116 * copies the target code into the newly created
123117 * isolated container
124118 */
125- copyContext (
126- containerID : string ,
127- language : language ,
128- context : CodeContext
129- ) : Promise < string > {
119+ copyContext ( ) : Promise < string > {
130120 return new Promise ( async ( resolve , reject ) => {
131- this . writeFile ( language , context )
121+ if ( ! this . containerID ) {
122+ reject ( "ContainerID not found." ) ;
123+ }
124+ this . writeFile ( )
132125 . then ( ( { filePath, fileFormat } ) => {
133- const initCommand = `docker cp ${ filePath } ${ containerID } :/src/target.${ fileFormat } ` ;
126+ const initCommand = `docker cp ${ filePath } ${ this . containerID } :/src/target.${ fileFormat } ` ;
134127 child . exec ( initCommand , ( error , containerID , stderr ) => {
135128 if ( error ) {
136129 reject ( error . message ) ;
@@ -152,19 +145,17 @@ class JobWorker {
152145 * 1] Creates a new container
153146 * 2] Copies the code into the contaienr
154147 */
155- initContainer ( language : language , context : CodeContext ) : Promise < JobStatus > {
148+ initContainer ( ) : Promise < JobStatus > {
156149 return new Promise ( async ( resolve , reject ) => {
157- this . createContainer ( language )
158- . then ( async ( { containerID , error, errorMessage } ) => {
150+ this . createContainer ( )
151+ . then ( async ( { error, errorMessage } ) => {
159152 if ( error ) return new Error ( errorMessage ) ;
160- return this . copyContext ( containerID , language , context )
153+ return this . copyContext ( )
161154 . then ( ( ) => {
162155 resolve ( {
163156 message : `Job has succedded.` ,
164157 error : false ,
165158 retryable : true ,
166- context : context ,
167- containerID : containerID ,
168159 } ) ;
169160 } )
170161 . catch ( ( e ) => {
@@ -176,7 +167,6 @@ class JobWorker {
176167 message : `Job has failed for the following reason(s): ${ e } .` ,
177168 jobFailed : true ,
178169 retryable : true ,
179- context : context ,
180170 } ) ;
181171 } ) ;
182172 } ) ;
@@ -187,12 +177,12 @@ class JobWorker {
187177 * 1] Spin up the container
188178 * 2] Record the output from the container
189179 */
190- startContainer ( language : language , context : CodeContext ) : Promise < ExecuteContainer > {
180+ startContainer ( ) : Promise < ExecuteContainer > {
191181 return new Promise ( ( resolve , reject ) => {
192- this . initContainer ( language , context )
182+ this . initContainer ( )
193183 . then ( ( jobStatus : JobStatus ) => {
194- if ( ! jobStatus . error && jobStatus . containerID ) {
195- const startContainer = `docker start -a ${ jobStatus . containerID } ` ;
184+ if ( ! jobStatus . error && this . containerID ) {
185+ const startContainer = `docker start -a ${ this . containerID } ` ;
196186 child . exec ( startContainer , ( error , stdout , stderr ) => {
197187 if ( error ) {
198188 reject ( {
@@ -208,7 +198,6 @@ class JobWorker {
208198 resolve ( {
209199 error : false ,
210200 codeOutput : stdout . trim ( ) ,
211- containerID : jobStatus . containerID
212201 } as ExecuteContainer ) ;
213202 }
214203 } ) ;
@@ -223,6 +212,62 @@ class JobWorker {
223212 . catch ( _ => { } ) ;
224213 } ) ;
225214 }
215+
216+
217+
218+ /**
219+ * Removes a container with the provided containerID
220+ */
221+ async removeContainer ( ) : Promise < Stdout > {
222+ return new Promise ( ( resolve , reject ) => {
223+ if ( ! this . containerID ) {
224+ reject ( "ContainerID not found." ) ;
225+ }
226+ const removeContainer = `docker rm --force ${ this . containerID } ` ;
227+ child . exec ( removeContainer , ( error , stdout , stderr ) => {
228+ if ( error ) {
229+ reject ( error . message ) ;
230+ } else if ( stderr ) {
231+ reject ( stderr ) ;
232+ } else {
233+ resolve ( stdout ) ;
234+ }
235+ } ) ;
236+ } ) ;
237+ }
238+
239+
240+ /**
241+ * Deletes the temp file in /temp folder
242+ */
243+ async removeCacheFile ( ) : Promise < Stdout > {
244+ return new Promise ( ( resolve , reject ) => {
245+ if ( ! this . filename ) {
246+ return reject ( "Filename not found." ) ;
247+ }
248+ const removeContainer = `rm ${ __dirname } /../temp/${ this . filename } ` ;
249+ child . exec ( removeContainer , ( error , stdout , stderr ) => {
250+ if ( error ) {
251+ reject ( error . message ) ;
252+ } else if ( stderr ) {
253+ reject ( stderr ) ;
254+ } else {
255+ resolve ( stdout ) ;
256+ }
257+ } ) ;
258+ } ) ;
259+ }
260+
261+ /**
262+ * Cleans up the job
263+ * Destroys the docker container & deletes cache files
264+ */
265+ async cleanupJob ( ) : Promise < any > {
266+ const jobs : Promise < any > [ ] = [ ]
267+ jobs . push ( this . removeContainer ( ) )
268+ jobs . push ( this . removeCacheFile ( ) )
269+ return Promise . all ( jobs )
270+ }
226271}
227272
228273export default JobWorker ;
0 commit comments