diff --git a/helpers/Dojo/ExerciseDockerCompose.ts b/helpers/Dojo/ExerciseDockerCompose.ts index 058c9e965228699406e35d100636966366274608..8d766aabf6c34f02b954fad0e3a4a3d6bbe40b4f 100644 --- a/helpers/Dojo/ExerciseDockerCompose.ts +++ b/helpers/Dojo/ExerciseDockerCompose.ts @@ -1,8 +1,9 @@ -import AssignmentFile from '../../../shared/types/Dojo/AssignmentFile'; -import { TypedEmitter } from 'tiny-typed-emitter'; -import ExerciseRunningEvents from '../../types/Dojo/ExerciseRunningEvents'; -import { spawn } from 'child_process'; -import ExerciseCheckerError from '../../../shared/types/Dojo/ExerciseCheckerError'; +import AssignmentFile from '../../../shared/types/Dojo/AssignmentFile'; +import { TypedEmitter } from 'tiny-typed-emitter'; +import ExerciseRunningEvents from '../../types/Dojo/ExerciseRunningEvents'; +import { spawn } from 'child_process'; +import ExerciseCheckerError from '../../../shared/types/Dojo/ExerciseCheckerError'; +import { ChildProcessWithoutNullStreams } from 'node:child_process'; class ExerciseDockerCompose { @@ -28,6 +29,20 @@ class ExerciseDockerCompose { }); } + private registerChildProcess(childProcess: ChildProcessWithoutNullStreams, resolve: (value: (number | PromiseLike<number>)) => void, reject: (reason?: unknown) => void) { + childProcess.stdout.on('data', (data) => { + this.events.emit('logs', data.toString(), false, false); + }); + + childProcess.stderr.on('data', (data) => { + this.events.emit('logs', data.toString(), true, false); + }); + + childProcess.on('exit', (code) => { + code !== null ? resolve(code) : reject(); + }); + } + run(doDown: boolean = false) { (async () => { let containerExitCode: number = -1; @@ -52,17 +67,7 @@ class ExerciseDockerCompose { } }); - dockerCompose.stdout.on('data', (data) => { - this.events.emit('logs', data.toString(), false, true); - }); - - dockerCompose.stderr.on('data', (data) => { - this.events.emit('logs', data.toString(), true, true); - }); - - dockerCompose.on('exit', (code) => { - code !== null ? resolve(code) : reject(); - }); + this.registerChildProcess(dockerCompose, resolve, reject); }); } catch ( error ) { this.events.emit('endStep', 'COMPOSE_RUN', `Error while running the docker compose file`, true); @@ -77,7 +82,7 @@ class ExerciseDockerCompose { try { this.events.emit('step', 'COMPOSE_LOGS', 'Linked services logs acquisition'); - await new Promise<void>((resolve, reject) => { + await new Promise<number>((resolve, reject) => { this.events.emit('logs', '####################################################### Other Services Logs #######################################################\n', false, false); @@ -86,17 +91,7 @@ class ExerciseDockerCompose { shell: true }); - dockerCompose.stdout.on('data', (data) => { - this.events.emit('logs', data.toString(), false, false); - }); - - dockerCompose.stderr.on('data', (data) => { - this.events.emit('logs', data.toString(), true, false); - }); - - dockerCompose.on('exit', (code) => { - code !== null ? resolve() : reject(); - }); + this.registerChildProcess(dockerCompose, resolve, reject); }); } catch ( error ) { this.events.emit('endStep', 'COMPOSE_LOGS', `Error while getting the linked services logs`, true); @@ -112,26 +107,16 @@ class ExerciseDockerCompose { try { this.events.emit('step', 'COMPOSE_DOWN', 'Stopping and removing containers'); - await new Promise<void>((resolve, reject) => { + await new Promise<number>((resolve, reject) => { this.events.emit('logs', '####################################################### Stop and remove containers #######################################################\n', false, false); - const dockerCompose = spawn(`${ dockerComposeCommand } down --volumes`, { + const dockerCompose = spawn(`${ dockerComposeCommand } down --volumes --rmi`, { cwd : this.executionFolder, shell: true }); - dockerCompose.stdout.on('data', (data) => { - this.events.emit('logs', data.toString(), false, false); - }); - - dockerCompose.stderr.on('data', (data) => { - this.events.emit('logs', data.toString(), true, false); - }); - - dockerCompose.on('exit', (code) => { - code !== null ? resolve() : reject(); - }); + this.registerChildProcess(dockerCompose, resolve, reject); }); } catch ( error ) { this.events.emit('endStep', 'COMPOSE_DOWN', `Error stop and remove containers`, true); @@ -142,6 +127,32 @@ class ExerciseDockerCompose { } } + // Remove images if asked + { + if ( doDown ) { + try { + this.events.emit('step', 'COMPOSE_REMOVE_DANGLING', 'Removing dangling images'); + + await new Promise<number>((resolve, reject) => { + + this.events.emit('logs', '####################################################### Remove dangling images #######################################################\n', false, false); + + const dockerCompose = spawn(`docker image prune --force`, { + cwd : this.executionFolder, + shell: true + }); + + this.registerChildProcess(dockerCompose, resolve, reject); + }); + } catch ( error ) { + this.events.emit('endStep', 'COMPOSE_REMOVE_DANGLING', `Error while removing dangling images`, true); + this.events.emit('finished', false, ExerciseCheckerError.DOCKER_COMPOSE_REMOVE_DANGLING_ERROR); + return; + } + this.events.emit('endStep', 'COMPOSE_REMOVE_DANGLING', `Dangling images removed`, false); + } + } + this.events.emit('finished', true, containerExitCode); })(); }