@@ -8,6 +8,7 @@ const docker = require('./docker');
88const log = require ( './log' ) ;
99const metrics = require ( './metrics' ) ;
1010const streams = require ( './streams' ) ;
11+ const tasks = require ( './tasks' ) ;
1112
1213// List available user machines for each project, create when necessary.
1314exports . getAvailableMachines = function ( user ) {
@@ -195,6 +196,56 @@ exports.spawn = function (user, projectId, callback) {
195196
196197 const machine = getOrCreateNewMachine ( user , projectId ) ;
197198
199+ _spawn ( machine , project , function ( error , machine ) {
200+ if ( error ) {
201+ callback ( error ) ;
202+ return ;
203+ }
204+
205+ const containerId = machine . docker . container ;
206+ // Quickly authorize the user's public SSH keys to access this container.
207+ deploySSHAuthorizedKeys ( user , machine , error => {
208+ log ( 'spawn-sshkeys' , containerId . slice ( 0 , 16 ) , error || 'success' ) ;
209+ db . save ( ) ;
210+ } ) ;
211+
212+ // Install all non-empty user configuration files into this container.
213+ Object . keys ( user . configurations ) . forEach ( file => {
214+ if ( ! user . configurations [ file ] ) {
215+ return ;
216+ }
217+ exports . deployConfiguration ( user , machine , file ) . then ( ( ) => {
218+ log ( 'spawn-config' , file , containerId . slice ( 0 , 16 ) , 'success' ) ;
219+ } ) . catch ( error => {
220+ log ( 'spawn-config' , file , containerId . slice ( 0 , 16 ) , error ) ;
221+ } ) ;
222+ } ) ;
223+ callback ( null , machine ) ;
224+ } ) ;
225+ } ;
226+
227+ // Instantiate a new temporary machine for a project. (Fast!)
228+ exports . spawnTemporary = function ( sessionId , projectId , callback ) {
229+ const machine = createNewTemporaryMachine ( sessionId , projectId ) ;
230+
231+ _spawn ( machine , getProject ( projectId ) , ( error , machine ) => {
232+ if ( error ) {
233+ callback ( error ) ;
234+ return ;
235+ }
236+
237+ const destroyDate = new Date ( Date . now ( ) ) ;
238+ destroyDate . setHours ( destroyDate . getHours ( ) + 8 ) ;
239+ tasks . add ( destroyDate , 'destroy-temporary' , {
240+ session : sessionId ,
241+ container : machine . docker . container
242+ } ) ;
243+
244+ callback ( null , machine ) ;
245+ } ) ;
246+ } ;
247+
248+ function _spawn ( machine , project , callback ) {
198249 // Keep track of the last project update this machine will be based on.
199250 metrics . set ( machine , 'updated' , project . data . updated ) ;
200251
@@ -217,7 +268,7 @@ exports.spawn = function (user, projectId, callback) {
217268 log ( 'spawn' , image , error ) ;
218269 machine . status = 'start-failed' ;
219270 db . save ( ) ;
220- callback ( new Error ( 'Unable to start machine for project: ' + projectId ) ) ;
271+ callback ( new Error ( 'Unable to start machine for project: ' + project . id ) ) ;
221272 return ;
222273 }
223274
@@ -230,27 +281,9 @@ exports.spawn = function (user, projectId, callback) {
230281 metrics . push ( project , 'spawn-time' , [ now , now - time ] ) ;
231282 db . save ( ) ;
232283
233- // Quickly authorize the user's public SSH keys to access this container.
234- deploySSHAuthorizedKeys ( user , machine , error => {
235- log ( 'spawn-sshkeys' , container . id . slice ( 0 , 16 ) , error || 'success' ) ;
236- db . save ( ) ;
237- } ) ;
238-
239- // Install all non-empty user configuration files into this container.
240- Object . keys ( user . configurations ) . forEach ( file => {
241- if ( ! user . configurations [ file ] ) {
242- return ;
243- }
244- exports . deployConfiguration ( user , machine , file ) . then ( ( ) => {
245- log ( 'spawn-config' , file , container . id . slice ( 0 , 16 ) , 'success' ) ;
246- } ) . catch ( error => {
247- log ( 'spawn-config' , file , container . id . slice ( 0 , 16 ) , error ) ;
248- } ) ;
249- } ) ;
250-
251284 callback ( null , machine ) ;
252285 } ) ;
253- } ;
286+ }
254287
255288// Destroy a given user machine and recycle its ports.
256289exports . destroy = function ( user , projectId , machineId , callback ) {
@@ -267,7 +300,7 @@ exports.destroy = function (user, projectId, machineId, callback) {
267300 return ;
268301 }
269302
270- const { container : containerId , host } = machine . docker ;
303+ const containerId = machine . docker . container ;
271304 if ( ! containerId ) {
272305 // This machine has no associated container, just recycle it as is.
273306 machine . status = 'new' ;
@@ -276,19 +309,55 @@ exports.destroy = function (user, projectId, machineId, callback) {
276309 return ;
277310 }
278311
279- log ( 'destroy' , containerId . slice ( 0 , 16 ) , 'started' ) ;
280- docker . removeContainer ( { host, container : containerId } , error => {
312+ _destroy ( machine , ( error ) => {
281313 if ( error ) {
282- log ( 'destroy' , containerId . slice ( 0 , 16 ) , error ) ;
283314 callback ( error ) ;
284- return ;
285315 }
286316
287317 // Recycle the machine's name and ports.
288318 machine . status = 'new' ;
289319 machine . docker . container = '' ;
290320 db . save ( ) ;
291321 callback ( ) ;
322+ } ) ;
323+ } ;
324+
325+ exports . destroyTemporary = function ( sessionId , containerId , callback ) {
326+ const machines = db . get ( 'temporaryMachines' ) [ sessionId ] ;
327+ if ( ! machines ) {
328+ callback ( new Error ( 'No machines for session ' + sessionId ) ) ;
329+ }
330+
331+ const machineIndex = machines . findIndex ( machine =>
332+ machine . docker . container === containerId ) ;
333+
334+ if ( machineIndex === - 1 ) {
335+ callback ( new Error ( 'Wrong container ID' ) ) ;
336+ }
337+
338+ _destroy ( machines [ machineIndex ] , ( error ) => {
339+ if ( error ) {
340+ callback ( error ) ;
341+ }
342+
343+ machines . splice ( machineIndex , 1 ) ;
344+ db . save ( ) ;
345+ callback ( ) ;
346+ } ) ;
347+ } ;
348+
349+ function _destroy ( machine , callback ) {
350+ const { container : containerId , host } = machine . docker ;
351+
352+ log ( 'destroy' , containerId . slice ( 0 , 16 ) , 'started' ) ;
353+ docker . removeContainer ( { host, container : containerId } , error => {
354+ if ( error ) {
355+ log ( 'destroy' , containerId . slice ( 0 , 16 ) , error ) ;
356+ callback ( error ) ;
357+ return ;
358+ }
359+
360+ callback ( ) ;
292361
293362 if ( ! machine . docker . image ) {
294363 log ( 'destroy' , containerId . slice ( 0 , 16 ) , 'success' ) ;
@@ -307,7 +376,7 @@ exports.destroy = function (user, projectId, machineId, callback) {
307376 db . save ( ) ;
308377 } ) ;
309378 } ) ;
310- } ;
379+ }
311380
312381// Install or overwrite a configuration file in all the user's containers.
313382exports . deployConfigurationInAllContainers = function ( user , file ) {
@@ -452,6 +521,33 @@ function getOrCreateNewMachine (user, projectId) {
452521 return machine ;
453522}
454523
524+ function createNewTemporaryMachine ( sessionId , projectId ) {
525+ const project = getProject ( projectId ) ;
526+ const temporaryMachines = db . get ( 'temporaryMachines' ) ;
527+ if ( ! ( sessionId in temporaryMachines ) ) {
528+ temporaryMachines [ sessionId ] = [ ] ;
529+ }
530+
531+ const machines = temporaryMachines [ sessionId ] ;
532+
533+ const machine = {
534+ properties : {
535+ name : project . name + ' #' + machines . length ,
536+ } ,
537+ status : 'new' ,
538+ docker : {
539+ host : '' ,
540+ container : '' ,
541+ ports : { } ,
542+ logs : ''
543+ } ,
544+ data : { }
545+ } ;
546+ machines . push ( machine ) ;
547+
548+ return machine ;
549+ }
550+
455551// Get a unique available port starting from 42000.
456552function getPort ( ) {
457553 const ports = db . get ( 'ports' ) ;
0 commit comments