diff --git a/src/api/process-manager.ts b/src/api/process-manager.ts index bb2d16eab..9f647d4a9 100644 --- a/src/api/process-manager.ts +++ b/src/api/process-manager.ts @@ -221,7 +221,7 @@ export function stopJob(jobId: number): Promise { }); } -function queueJob(buildId: number, jobId: number, ssh = false): Promise { +function queueJob(buildId: number, jobId: number, sshAndVnc = false): Promise { let commands: string[] = null; let processes: JobProcess[] = null; @@ -245,7 +245,7 @@ function queueJob(buildId: number, jobId: number, ssh = false): Promise { build_id: buildId, job_id: jobId, status: 'queued', - job: prepareJob(buildId, jobId, commands, ssh), + job: prepareJob(buildId, jobId, commands, sshAndVnc), log: [] }; @@ -255,7 +255,7 @@ function queueJob(buildId: number, jobId: number, ssh = false): Promise { }); } -function prepareJob(buildId: number, jobId: number, cmds: any, ssh = false): +function prepareJob(buildId: number, jobId: number, cmds: any, sshAndVnc = false): Observable { return new Observable(observer => { getJobProcesses().then(processes => { @@ -264,7 +264,7 @@ function prepareJob(buildId: number, jobId: number, cmds: any, ssh = false): jobProcesses.next(processes); const process = processes[index]; - startBuildProcess(buildId, jobId, cmds, 'abstruse', ssh).subscribe(event => { + startBuildProcess(buildId, jobId, cmds, 'abstruse', sshAndVnc).subscribe(event => { const msg: JobMessage = { build_id: buildId, job_id: jobId, @@ -361,35 +361,19 @@ export function restartJob(jobId: number): Promise { }); } -export function restartJobWithSSH(jobId: number): Promise { - return getJobProcesses() - .then(procs => { - const jobProcess = procs.find(job => job.job_id === jobId); - if (jobProcess) { - let jobData; - return dbJob.resetJob(jobId) - .then(job => { - jobData = job; - jobEvents.next({ - type: 'process', - build_id: job.builds_id, - job_id: job.id, - data: 'jobRestarted' - }); - }) - .then(() => queueJob(jobData.builds_id, jobData.id, true)); - } else { - return dbJob.getJob(jobId).then(job => { - jobEvents.next({ - type: 'process', - build_id: job.builds_id, - job_id: job.id, - data: 'jobRestarted' - }); - - return queueJob(job.builds_id, job.id, true); - }); - } +export function restartJobWithSshAndVnc(jobId: number): Promise { + let jobData = null; + return stopJob(jobId) + .then(() => dbJob.resetJob(jobId)) + .then(job => jobData = job) + .then(() => queueJob(jobData.builds_id, jobId, true)) + .then(() => { + jobEvents.next({ + type: 'process', + build_id: jobData.builds_id, + job_id: jobData.id, + data: 'jobRestarted' + }); }); } diff --git a/src/api/process.ts b/src/api/process.ts index 7564a094f..702d36e23 100644 --- a/src/api/process.ts +++ b/src/api/process.ts @@ -28,7 +28,7 @@ export interface ProcessOutput { } export function startBuildProcess(buildId: number, jobId: number, - commands: string[], image: string, ssh = false): Observable { + commands: string[], image: string, sshAndVnc = false): Observable { return new Observable(observer => { const name = 'abstruse_' + buildId + '_' + jobId; const vars = commands.filter(cmd => cmd.startsWith('export')) @@ -38,9 +38,22 @@ export function startBuildProcess(buildId: number, jobId: number, }, []); commands = commands.filter(cmd => !cmd.startsWith('export')); + let debug: Observable = Observable.empty(); + if (sshAndVnc) { + debug = Observable.concat(...[ + executeInContainer(name, 'sudo /etc/init.d/ssh start'), + getContainerExposedPort(name, 22), + executeInContainer(name, 'export DISPLAY=:99 && sudo /etc/init.d/xvfb start && ' + + 'sleep 3 && sudo /etc/init.d/openbox start'), + executeInContainer(name, 'export DISPLAY=:99 && ' + + 'x11vnc -xkb -noxrecord -noxfixes -noxdamage -display :99 ' + + '-forever -bg -rfbauth /etc/x11vnc.pass -rfbport 5900'), + getContainerExposedPort(name, 5900) + ]); + } + const sub = startContainer(name, image, vars) - .concat(ssh ? executeInContainer(name, 'sudo /etc/init.d/ssh start') : Observable.empty()) - .concat(ssh ? getContainerExposedPort(name, 22) : Observable.empty()) + .concat(debug) .concat(...commands.map(command => executeInContainer(name, command))) .subscribe((event: ProcessOutput) => { observer.next(event); @@ -166,7 +179,9 @@ function getContainerExposedPort(name: string, port: number): Observable observer.next({ type: 'exposedPort', data: data.split(':')[1] })); + process.on('data', data => { + return observer.next({ type: 'exposedPort', data: port + ':' + data.split(':')[1] }); + }); process.on('exit', () => observer.complete()); }); } diff --git a/src/api/socket.ts b/src/api/socket.ts index fc9c27241..8bd9e2ea0 100644 --- a/src/api/socket.ts +++ b/src/api/socket.ts @@ -10,7 +10,7 @@ import { findDockerImageBuildJob, jobEvents, restartJob, - restartJobWithSSH, + restartJobWithSshAndVnc, stopJob, restartBuild, stopBuild, @@ -107,8 +107,8 @@ export class SocketServer { conn.next({ type: 'jobRestarted', data: event.data.jobId }); }); break; - case 'restartJobWithSSH': - restartJobWithSSH(parseInt(event.data.jobId, 10)) + case 'restartJobWithSshAndVnc': + restartJobWithSshAndVnc(parseInt(event.data.jobId, 10)) .then(() => { conn.next({ type: 'jobRestarted', data: event.data.jobId }); }); diff --git a/src/app/components/app-job/app-job.component.html b/src/app/components/app-job/app-job.component.html index 6d80a599d..655401645 100644 --- a/src/app/components/app-job/app-job.component.html +++ b/src/app/components/app-job/app-job.component.html @@ -90,13 +90,14 @@

{{ job?.build?.message }}

-
+
SSH Daemon is enabled in this container. ssh {{ sshd.split(':')[0] }} -p {{ sshd.split(':')[1] }} -l abstruse + VNC: {{ vnc.split(':')[0] }} VNC Port: {{ vnc.split(':')[1] }} password: abstruse
diff --git a/src/app/components/app-job/app-job.component.ts b/src/app/components/app-job/app-job.component.ts index 197ba8d08..fb758dbd2 100644 --- a/src/app/components/app-job/app-job.component.ts +++ b/src/app/components/app-job/app-job.component.ts @@ -27,6 +27,7 @@ export class AppJobComponent implements OnInit, OnDestroy { previousRuntime: number; processing: boolean; sshd: string; + vnc: string; expectedProgress: number; constructor( @@ -52,7 +53,11 @@ export class AppJobComponent implements OnInit, OnDestroy { } else if (event.type === 'jobRestarted' && event.data === this.id) { this.processing = false; } else if (event.type === 'exposedPort') { - this.sshd = `${document.location.hostname}:${event.data}`; + if (parseInt(event.data.split(':')[0], 10) === 22) { + this.sshd = `${document.location.hostname}:${event.data.split(':')[1]}`; + } else if (parseInt(event.data.split(':')[0], 10) === 5900) { + this.vnc = `${document.location.hostname}:${event.data.split(':')[1]}`; + } } }); @@ -120,6 +125,7 @@ export class AppJobComponent implements OnInit, OnDestroy { this.terminalInput = { clear: true }; this.processing = true; this.sshd = null; + this.vnc = null; this.socketService.emit({ type: 'restartJob', data: { jobId: this.id } }); } @@ -129,7 +135,8 @@ export class AppJobComponent implements OnInit, OnDestroy { this.terminalInput = { clear: true }; this.processing = true; this.sshd = null; - this.socketService.emit({ type: 'restartJobWithSSH', data: { jobId: this.id } }); + this.vnc = null; + this.socketService.emit({ type: 'restartJobWithSshAndVnc', data: { jobId: this.id } }); } stopJob(e: MouseEvent): void { @@ -137,6 +144,7 @@ export class AppJobComponent implements OnInit, OnDestroy { e.stopPropagation(); this.processing = true; this.sshd = null; + this.vnc = null; this.socketService.emit({ type: 'stopJob', data: { jobId: this.id } }); }