1+ var fs = require ( 'fs' ) ,
2+ path = require ( 'path' ) ,
3+ rimraf = require ( 'rimraf' ) ,
4+ _ = require ( 'lodash' ) ,
5+ spawn = require ( 'child_process' ) . spawn ;
6+
7+ var respondInvalidWorkspace = function ( res ) {
8+ res . status ( 400 ) ;
9+ res . json ( { msg : "Invalid workspace name" } ) ;
10+ } ;
11+
12+ var createWorkspace = function ( params , req , res ) {
13+ var potentiallyBadPathName = params . name . split ( path . sep ) ;
14+ var workspaceName = potentiallyBadPathName [ potentiallyBadPathName . length - 1 ] ;
15+
16+ if ( workspaceName === '..' ) {
17+ respondInvalidWorkspace ( res ) ;
18+ return ;
19+ }
20+
21+ fs . mkdir ( __dirname + '/../workspaces/' + req . user + "/" + workspaceName , '0700' , function ( err ) {
22+ if ( err ) {
23+ respondInvalidWorkspace ( res ) ;
24+ return ;
25+ }
26+
27+ res . json ( { msg : "Workspace " + workspaceName + " was created." } ) ;
28+ } ) ;
29+ }
30+
31+ var createWorkspaceKillTimeout = function ( req , workspaceProcess , workspaceName ) {
32+ var timeout = setTimeout ( function ( ) {
33+ process . kill ( - workspaceProcess . pid , 'SIGTERM' ) ;
34+ req . app . get ( 'runningWorkspaces' ) [ req . user + '/' + workspaceName ] = undefined ;
35+ console . info ( "Killed workspace " + workspaceName ) ;
36+ } , 900000 ) ; //Workspaces have a lifetime of 15 minutes
37+
38+ return timeout ;
39+ } ;
40+
41+ /*
42+ * POST/GET create a new workspace
43+ */
44+ exports . create = function ( req , res ) {
45+ if ( req . body . name ) {
46+ createWorkspace ( req . body , req , res ) ;
47+ } else {
48+ respondInvalidWorkspace ( res ) ;
49+ }
50+ }
51+
52+ /*
53+ * GET workspaces listing.
54+ */
55+ exports . list = function ( req , res ) {
56+ fs . readdir ( __dirname + '/../workspaces/' + req . user , function ( err , files ) {
57+ if ( err ) {
58+ res . status ( 500 ) ;
59+ res . json ( { error : err } ) ;
60+ } else {
61+ var workspaces = [ ] ;
62+ for ( var i = 0 ; i < files . length ; i ++ ) {
63+ // Skip hidden files
64+ if ( files [ i ] [ 0 ] === '.' ) continue ;
65+
66+ workspaces . push ( { name : files [ i ] } )
67+ }
68+ res . json ( { workspaces : workspaces } ) ;
69+ }
70+ } ) ;
71+ } ;
72+
73+ /**
74+ * DELETE destroys a workspace
75+ */
76+ exports . destroy = function ( req , res ) {
77+ var potentiallyBadPathName = req . params . name . split ( path . sep ) ;
78+ var workspaceName = potentiallyBadPathName [ potentiallyBadPathName . length - 1 ] ;
79+
80+ if ( workspaceName === '..' ) {
81+ respondInvalidWorkspace ( res ) ;
82+ return ;
83+ }
84+
85+ rimraf ( __dirname + "/../workspaces/" + req . user + "/" + workspaceName , function ( err ) {
86+ if ( err ) {
87+ res . status ( "500" ) ;
88+ res . json ( { msg : "Something went wrong :(" } ) ;
89+ return ;
90+ }
91+ res . json ( { msg : "Successfully deleted " + workspaceName } ) ;
92+ } )
93+ } ;
94+
95+ /*
96+ * GET run a workspace
97+ */
98+ exports . run = function ( req , res ) {
99+ var potentiallyBadPathName = req . params . name . split ( path . sep ) ;
100+ var workspaceName = potentiallyBadPathName [ potentiallyBadPathName . length - 1 ] ;
101+
102+ var isPortTaken = function ( port , fn ) {
103+ console . log ( 'checking if port' , port , 'is taken' ) ;
104+ var net = require ( 'net' )
105+ var tester = net . createServer ( )
106+ . once ( 'error' , function ( err ) {
107+ if ( err . code != 'EADDRINUSE' ) return fn ( err )
108+ console . log ( 'port' , port , 'seems to be taken' ) ;
109+ fn ( null , true )
110+ } )
111+ . once ( 'listening' , function ( ) {
112+ tester . once ( 'close' , function ( ) {
113+ console . log ( 'port' , port , 'seems to be available' ) ;
114+ fn ( null , false )
115+ } )
116+ . close ( )
117+ } )
118+ . listen ( port )
119+ } ;
120+
121+ var getNextAvailablePort = function ( callback ) {
122+ var nextFreeWorkspacePort = req . app . get ( 'nextFreeWorkspacePort' ) ;
123+
124+ if ( nextFreeWorkspacePort > 10000 ) {
125+ nextFreeWorkspacePort = 5000 ;
126+ }
127+
128+ nextFreeWorkspacePort = nextFreeWorkspacePort + 1 ;
129+ console . log ( 'setting nextFreeWorkspacePort to' , nextFreeWorkspacePort ) ;
130+ req . app . set ( 'nextFreeWorkspacePort' , nextFreeWorkspacePort ) ;
131+
132+ isPortTaken ( nextFreeWorkspacePort , function ( err , taken ) {
133+ if ( taken ) {
134+ getNextAvailablePort ( callback ) ;
135+ } else {
136+ req . app . set ( 'nextFreeWorkspacePort' , nextFreeWorkspacePort ) ;
137+ callback ( nextFreeWorkspacePort ) ;
138+ }
139+ } ) ;
140+ } ;
141+
142+ if ( workspaceName === '..' ) {
143+ respondInvalidWorkspace ( res ) ;
144+ return ;
145+ }
146+
147+ if ( typeof req . app . get ( 'runningWorkspaces' ) [ req . user + '/' + workspaceName ] === 'undefined' ) {
148+ getNextAvailablePort ( function ( nextFreePort ) {
149+ console . log ( "Starting " + __dirname + '/../../c9/bin/cloud9.sh for workspace ' + workspaceName + " on port " + nextFreePort ) ;
150+
151+ var workspace = spawn ( __dirname + '/../../c9/bin/cloud9.sh' , [ '-w' , __dirname + '/../workspaces/' + req . user + '/' + workspaceName , '-l' , '0.0.0.0' , '-p' , nextFreePort ] , { detached : true } ) ;
152+ workspace . stderr . on ( 'data' , function ( data ) {
153+ console . log ( 'stdERR: ' + data ) ;
154+ } ) ;
155+
156+ req . app . get ( 'runningWorkspaces' ) [ req . user + '/' + workspaceName ] = {
157+ killTimeout : createWorkspaceKillTimeout ( req , workspace , workspaceName ) ,
158+ process : workspace ,
159+ name : workspaceName ,
160+ url : req . app . settings . baseUrl + ":" + nextFreePort ,
161+ user : req . user
162+ } ;
163+
164+ res . json ( { msg : "Attempted to start workspace" , user : req . user , url : req . app . settings . baseUrl + ":" + nextFreePort } ) ;
165+ } ) ;
166+ } else {
167+ console . log ( "Found running workspace" , req . app . get ( 'runningWorkspaces' ) [ req . user + '/' + workspaceName ] . url ) ;
168+ res . json ( { msg : "Found running workspace" , user : req . user , url : req . app . get ( 'runningWorkspaces' ) [ req . user + '/' + workspaceName ] . url } ) ;
169+ }
170+
171+ }
172+
173+ /*
174+ * POST to keep the workspace alive
175+ */
176+ exports . keepAlive = function ( req , res ) {
177+ var workspace = req . app . get ( 'runningWorkspaces' ) [ req . user + '/' + req . params . name ] ;
178+ clearTimeout ( workspace . killTimeout ) ;
179+ workspace . killTimeout = createWorkspaceKillTimeout ( req , workspace . process , workspace . name ) ;
180+ res . send ( ) ;
181+ }
0 commit comments