@@ -8,6 +8,7 @@ const docker = require('./docker');
8
8
const log = require ( './log' ) ;
9
9
const metrics = require ( './metrics' ) ;
10
10
const streams = require ( './streams' ) ;
11
+ const tasks = require ( './tasks' ) ;
11
12
12
13
// Get an existing user machine with the given project and machine ID.
13
14
exports . getMachineById = function ( user , projectId , machineId ) {
@@ -185,6 +186,56 @@ exports.spawn = function (user, projectId, callback) {
185
186
186
187
const machine = getOrCreateNewMachine ( user , projectId ) ;
187
188
189
+ _spawn ( machine , project , function ( error , machine ) {
190
+ if ( error ) {
191
+ callback ( error ) ;
192
+ return ;
193
+ }
194
+
195
+ const containerId = machine . docker . container ;
196
+ // Quickly authorize the user's public SSH keys to access this container.
197
+ deploySSHAuthorizedKeys ( user , machine , error => {
198
+ log ( 'spawn-sshkeys' , containerId . slice ( 0 , 16 ) , error || 'success' ) ;
199
+ db . save ( ) ;
200
+ } ) ;
201
+
202
+ // Install all non-empty user configuration files into this container.
203
+ Object . keys ( user . configurations ) . forEach ( file => {
204
+ if ( ! user . configurations [ file ] ) {
205
+ return ;
206
+ }
207
+ exports . deployConfiguration ( user , machine , file ) . then ( ( ) => {
208
+ log ( 'spawn-config' , file , containerId . slice ( 0 , 16 ) , 'success' ) ;
209
+ } ) . catch ( error => {
210
+ log ( 'spawn-config' , file , containerId . slice ( 0 , 16 ) , error ) ;
211
+ } ) ;
212
+ } ) ;
213
+ callback ( null , machine ) ;
214
+ } ) ;
215
+ } ;
216
+
217
+ // Instantiate a new temporary machine for a project. (Fast!)
218
+ exports . spawnTemporary = function ( sessionId , projectId , callback ) {
219
+ const machine = createNewTemporaryMachine ( sessionId , projectId ) ;
220
+
221
+ _spawn ( machine , getProject ( projectId ) , ( error , machine ) => {
222
+ if ( error ) {
223
+ callback ( error ) ;
224
+ return ;
225
+ }
226
+
227
+ const destroyDate = new Date ( Date . now ( ) ) ;
228
+ destroyDate . setHours ( destroyDate . getHours ( ) + 8 ) ;
229
+ tasks . add ( destroyDate , 'destroy-temporary' , {
230
+ session : sessionId ,
231
+ container : machine . docker . container
232
+ } ) ;
233
+
234
+ callback ( null , machine ) ;
235
+ } ) ;
236
+ } ;
237
+
238
+ function _spawn ( machine , project , callback ) {
188
239
// Keep track of the last project update this machine will be based on.
189
240
metrics . set ( machine , 'updated' , project . data . updated ) ;
190
241
@@ -207,7 +258,7 @@ exports.spawn = function (user, projectId, callback) {
207
258
log ( 'spawn' , image , error ) ;
208
259
machine . status = 'start-failed' ;
209
260
db . save ( ) ;
210
- callback ( new Error ( 'Unable to start machine for project: ' + projectId ) ) ;
261
+ callback ( new Error ( 'Unable to start machine for project: ' + project . id ) ) ;
211
262
return ;
212
263
}
213
264
@@ -220,27 +271,9 @@ exports.spawn = function (user, projectId, callback) {
220
271
metrics . push ( project , 'spawn-time' , [ now , now - time ] ) ;
221
272
db . save ( ) ;
222
273
223
- // Quickly authorize the user's public SSH keys to access this container.
224
- deploySSHAuthorizedKeys ( user , machine , error => {
225
- log ( 'spawn-sshkeys' , container . id . slice ( 0 , 16 ) , error || 'success' ) ;
226
- db . save ( ) ;
227
- } ) ;
228
-
229
- // Install all non-empty user configuration files into this container.
230
- Object . keys ( user . configurations ) . forEach ( file => {
231
- if ( ! user . configurations [ file ] ) {
232
- return ;
233
- }
234
- exports . deployConfiguration ( user , machine , file ) . then ( ( ) => {
235
- log ( 'spawn-config' , file , container . id . slice ( 0 , 16 ) , 'success' ) ;
236
- } ) . catch ( error => {
237
- log ( 'spawn-config' , file , container . id . slice ( 0 , 16 ) , error ) ;
238
- } ) ;
239
- } ) ;
240
-
241
274
callback ( null , machine ) ;
242
275
} ) ;
243
- } ;
276
+ }
244
277
245
278
// Destroy a given user machine and recycle its ports.
246
279
exports . destroy = function ( user , projectId , machineId , callback ) {
@@ -257,7 +290,7 @@ exports.destroy = function (user, projectId, machineId, callback) {
257
290
return ;
258
291
}
259
292
260
- const { container : containerId , host } = machine . docker ;
293
+ const containerId = machine . docker . container ;
261
294
if ( ! containerId ) {
262
295
// This machine has no associated container, just recycle it as is.
263
296
machine . status = 'new' ;
@@ -266,19 +299,55 @@ exports.destroy = function (user, projectId, machineId, callback) {
266
299
return ;
267
300
}
268
301
269
- log ( 'destroy' , containerId . slice ( 0 , 16 ) , 'started' ) ;
270
- docker . removeContainer ( { host, container : containerId } , error => {
302
+ _destroy ( machine , ( error ) => {
271
303
if ( error ) {
272
- log ( 'destroy' , containerId . slice ( 0 , 16 ) , error ) ;
273
304
callback ( error ) ;
274
- return ;
275
305
}
276
306
277
307
// Recycle the machine's name and ports.
278
308
machine . status = 'new' ;
279
309
machine . docker . container = '' ;
280
310
db . save ( ) ;
281
311
callback ( ) ;
312
+ } ) ;
313
+ } ;
314
+
315
+ exports . destroyTemporary = function ( sessionId , containerId , callback ) {
316
+ const machines = db . get ( 'temporaryMachines' ) [ sessionId ] ;
317
+ if ( ! machines ) {
318
+ callback ( new Error ( 'No machines for session ' + sessionId ) ) ;
319
+ }
320
+
321
+ const machineIndex = machines . findIndex ( machine =>
322
+ machine . docker . container === containerId ) ;
323
+
324
+ if ( machineIndex === - 1 ) {
325
+ callback ( new Error ( 'Wrong container ID' ) ) ;
326
+ }
327
+
328
+ _destroy ( machines [ machineIndex ] , ( error ) => {
329
+ if ( error ) {
330
+ callback ( error ) ;
331
+ }
332
+
333
+ machines . splice ( machineIndex , 1 ) ;
334
+ db . save ( ) ;
335
+ callback ( ) ;
336
+ } ) ;
337
+ } ;
338
+
339
+ function _destroy ( machine , callback ) {
340
+ const { container : containerId , host } = machine . docker ;
341
+
342
+ log ( 'destroy' , containerId . slice ( 0 , 16 ) , 'started' ) ;
343
+ docker . removeContainer ( { host, container : containerId } , error => {
344
+ if ( error ) {
345
+ log ( 'destroy' , containerId . slice ( 0 , 16 ) , error ) ;
346
+ callback ( error ) ;
347
+ return ;
348
+ }
349
+
350
+ callback ( ) ;
282
351
283
352
if ( ! machine . docker . image ) {
284
353
log ( 'destroy' , containerId . slice ( 0 , 16 ) , 'success' ) ;
@@ -297,7 +366,7 @@ exports.destroy = function (user, projectId, machineId, callback) {
297
366
db . save ( ) ;
298
367
} ) ;
299
368
} ) ;
300
- } ;
369
+ }
301
370
302
371
// Install or overwrite a configuration file in all the user's containers.
303
372
exports . deployConfigurationInAllContainers = function ( user , file ) {
@@ -445,6 +514,33 @@ function getOrCreateNewMachine (user, projectId) {
445
514
return machine ;
446
515
}
447
516
517
+ function createNewTemporaryMachine ( sessionId , projectId ) {
518
+ const project = getProject ( projectId ) ;
519
+ const temporaryMachines = db . get ( 'temporaryMachines' ) ;
520
+ if ( ! ( sessionId in temporaryMachines ) ) {
521
+ temporaryMachines [ sessionId ] = [ ] ;
522
+ }
523
+
524
+ const machines = temporaryMachines [ sessionId ] ;
525
+
526
+ const machine = {
527
+ properties : {
528
+ name : project . name + ' #' + machines . length ,
529
+ } ,
530
+ status : 'new' ,
531
+ docker : {
532
+ host : '' ,
533
+ container : '' ,
534
+ ports : { } ,
535
+ logs : ''
536
+ } ,
537
+ data : { }
538
+ } ;
539
+ machines . push ( machine ) ;
540
+
541
+ return machine ;
542
+ }
543
+
448
544
// Get a unique available port starting from 42000.
449
545
function getPort ( ) {
450
546
const ports = db . get ( 'ports' ) ;
0 commit comments