@@ -35,8 +35,20 @@ io.on('connection', (socket) => {
3535
3636 const scenarioId = socket . handshake . query . scenarioId as string
3737
38+ if ( ! scenarioId ) {
39+ socket . emit ( 'output' , '\\r\\nError: No scenario ID provided\\r\\n' )
40+ socket . disconnect ( )
41+ return
42+ }
43+
44+ console . log ( `Starting scenario '${ scenarioId } ' for socket ${ socket . id } ` )
45+
3846 // Start a new container for this scenario
39- startScenarioContainer ( socket , scenarioId )
47+ startScenarioContainer ( socket , scenarioId ) . catch ( ( error ) => {
48+ console . error ( `Failed to start scenario ${ scenarioId } :` , error )
49+ socket . emit ( 'output' , `\\r\\nError: Failed to start scenario: ${ error . message } \\r\\n` )
50+ socket . disconnect ( )
51+ } )
4052
4153 socket . on ( 'input' , ( data : string ) => {
4254 const session = sessions . get ( socket . id )
@@ -58,7 +70,16 @@ io.on('connection', (socket) => {
5870 } )
5971} )
6072
61- function startScenarioContainer ( socket : any , scenarioId : string ) {
73+ async function checkDockerImage ( imageName : string ) : Promise < boolean > {
74+ return new Promise ( ( resolve ) => {
75+ const checkImage = spawn ( 'docker' , [ 'image' , 'inspect' , imageName ] )
76+ checkImage . on ( 'exit' , ( code ) => {
77+ resolve ( code === 0 )
78+ } )
79+ } )
80+ }
81+
82+ async function startScenarioContainer ( socket : any , scenarioId : string ) {
6283 // Map scenario IDs to Docker images
6384 const scenarioImages : Record < string , string > = {
6485 'k8s-crashloop' : 'devopslearn/scenario-keycloak-crashloop' ,
@@ -69,15 +90,35 @@ function startScenarioContainer(socket: any, scenarioId: string) {
6990
7091 const imageName = scenarioImages [ scenarioId ] || 'devopslearn/scenario-base'
7192
72- // Start Docker container with TTY
73- const dockerProcess = spawn ( 'docker' , [
93+ // Check if Docker image exists
94+ const imageExists = await checkDockerImage ( imageName )
95+ if ( ! imageExists ) {
96+ socket . emit ( 'output' , `\r\nError: Docker image '${ imageName } ' not found.\r\n` )
97+ socket . emit ( 'output' , `Please run 'make scenario-build' to build the scenario images.\r\n` )
98+ socket . emit ( 'output' , `\r\nDisconnecting...\r\n` )
99+ socket . disconnect ( )
100+ return
101+ }
102+
103+ // Build Docker run arguments
104+ const args = [
74105 'run' ,
75106 '-it' ,
76107 '--rm' ,
77108 '--name' , `devops-dojo-${ socket . id } ` ,
78109 '--network' , 'devops-dojo-net' ,
79- imageName
80- ] , {
110+ ]
111+
112+ // Mount Docker socket for Kubernetes scenarios that need it
113+ const k8sScenarios = [ 'k8s-crashloop' , 'k8s-dns' , 'k8s-istio' ]
114+ if ( k8sScenarios . includes ( scenarioId ) ) {
115+ args . push ( '-v' , '/var/run/docker.sock:/var/run/docker.sock' )
116+ }
117+
118+ args . push ( imageName )
119+
120+ // Start Docker container with TTY
121+ const dockerProcess = spawn ( 'docker' , args , {
81122 env : { ...process . env , TERM : 'xterm-256color' }
82123 } )
83124
@@ -94,31 +135,115 @@ function startScenarioContainer(socket: any, scenarioId: string) {
94135 } )
95136
96137 dockerProcess . stderr . on ( 'data' , ( data ) => {
97- socket . emit ( 'output' , data . toString ( ) )
138+ const errorMessage = data . toString ( )
139+ console . error ( `Container ${ scenarioId } stderr:` , errorMessage )
140+ socket . emit ( 'output' , errorMessage )
141+ } )
142+
143+ dockerProcess . on ( 'error' , ( error ) => {
144+ console . error ( `Failed to start container for scenario ${ scenarioId } :` , error )
145+ socket . emit ( 'output' , `\\r\\nError: Failed to start container: ${ error . message } \\r\\n` )
146+ sessions . delete ( socket . id )
98147 } )
99148
100149 dockerProcess . on ( 'exit' , ( code ) => {
150+ console . log ( `Container for scenario ${ scenarioId } exited with code ${ code } ` )
101151 socket . emit ( 'output' , `\\r\\nContainer exited with code ${ code } \\r\\n` )
102152 sessions . delete ( socket . id )
103153 } )
104154
105- // Notify client that scenario is ready
155+ // Notify client that scenario is ready after a short delay
156+ // TODO: Replace with actual readiness check
106157 setTimeout ( ( ) => {
107- socket . emit ( 'scenario-ready' )
158+ // Check if container is still running
159+ if ( sessions . has ( socket . id ) ) {
160+ socket . emit ( 'scenario-ready' )
161+ console . log ( `Scenario ${ scenarioId } is ready for socket ${ socket . id } ` )
162+ }
108163 } , 2000 )
109164}
110165
111166function cleanupSession ( socketId : string ) {
112167 const session = sessions . get ( socketId )
113168 if ( session ) {
169+ console . log ( `Cleaning up session for ${ socketId } , scenario: ${ session . scenarioId } ` )
114170 // Kill the Docker container
115- spawn ( 'docker' , [ 'kill' , session . containerId ] )
171+ const killProcess = spawn ( 'docker' , [ 'kill' , session . containerId ] )
172+ killProcess . on ( 'error' , ( error ) => {
173+ console . error ( `Error killing container ${ session . containerId } :` , error )
174+ } )
116175 sessions . delete ( socketId )
117176 }
118177}
119178
179+ // Check if Docker daemon is accessible
180+ async function checkDockerDaemon ( ) : Promise < boolean > {
181+ return new Promise ( ( resolve ) => {
182+ const checkDocker = spawn ( 'docker' , [ 'version' ] )
183+ checkDocker . on ( 'exit' , ( code ) => {
184+ resolve ( code === 0 )
185+ } )
186+ checkDocker . on ( 'error' , ( ) => {
187+ resolve ( false )
188+ } )
189+ } )
190+ }
191+
192+ // Ensure Docker network exists
193+ async function ensureDockerNetwork ( ) {
194+ try {
195+ // Check if network exists
196+ const checkNetwork = spawn ( 'docker' , [ 'network' , 'inspect' , 'devops-dojo-net' ] )
197+
198+ await new Promise ( ( resolve ) => {
199+ checkNetwork . on ( 'exit' , ( code ) => {
200+ if ( code !== 0 ) {
201+ // Network doesn't exist, create it
202+ console . log ( 'Creating Docker network: devops-dojo-net' )
203+ const createNetwork = spawn ( 'docker' , [ 'network' , 'create' , 'devops-dojo-net' ] )
204+ createNetwork . on ( 'exit' , ( createCode ) => {
205+ if ( createCode === 0 ) {
206+ console . log ( 'Docker network created successfully' )
207+ } else {
208+ console . error ( 'Failed to create Docker network' )
209+ }
210+ resolve ( null )
211+ } )
212+ } else {
213+ console . log ( 'Docker network already exists' )
214+ resolve ( null )
215+ }
216+ } )
217+ } )
218+ } catch ( error ) {
219+ console . error ( 'Error checking/creating Docker network:' , error )
220+ }
221+ }
222+
120223const PORT = process . env . PORT || 3001
121224
122- server . listen ( PORT , ( ) => {
123- console . log ( `DevOps Dojo server running on port ${ PORT } ` )
225+ // Initialize server with Docker checks
226+ async function initializeServer ( ) {
227+ console . log ( 'Checking Docker daemon...' )
228+ const dockerAvailable = await checkDockerDaemon ( )
229+
230+ if ( ! dockerAvailable ) {
231+ console . error ( 'Error: Docker daemon is not accessible' )
232+ console . error ( 'Please ensure Docker is installed and running' )
233+ process . exit ( 1 )
234+ }
235+
236+ console . log ( 'Docker daemon is accessible' )
237+
238+ await ensureDockerNetwork ( )
239+
240+ server . listen ( PORT , ( ) => {
241+ console . log ( `DevOps Dojo server running on port ${ PORT } ` )
242+ } )
243+ }
244+
245+ // Start the server
246+ initializeServer ( ) . catch ( ( error ) => {
247+ console . error ( 'Failed to initialize server:' , error )
248+ process . exit ( 1 )
124249} )
0 commit comments