diff --git a/src/api/process.ts b/src/api/process.ts index bf4e0b34b..e3c34f812 100644 --- a/src/api/process.ts +++ b/src/api/process.ts @@ -39,8 +39,8 @@ export function startBuildProcess(buildId: number, jobId: number, commands = commands.filter(cmd => !cmd.startsWith('export')); startContainer(name, image, vars) - .concat(ssh ? executeInContainer(name, 'sudo /etc/init.d/ssh start') : null) - .concat(ssh ? getContainerExposedPort(name, 22) : null) + .concat(ssh ? executeInContainer(name, 'sudo /etc/init.d/ssh start') : Observable.empty()) + .concat(ssh ? getContainerExposedPort(name, 22) : Observable.empty()) .concat(...commands.map(command => executeInContainer(name, command))) .subscribe((event: ProcessOutput) => { observer.next(event); @@ -57,9 +57,14 @@ export function startBuildProcess(buildId: number, jobId: number, function executeInContainer(name: string, command: string): Observable { return new Observable(observer => { - const start = nodePty.spawn('docker', ['start', name]); + const start = nodePty.spawn('docker', ['start', name], { name: 'xterm-color' }); + + start.on('exit', startCode => { + if (startCode !== 0) { + observer.error(bold(red('Container errored with exit code ' + startCode))); + } + - start.on('exit', () => { let exitCode = 255; let executed = false; let attach = null; @@ -81,6 +86,7 @@ function executeInContainer(name: string, command: string): Observable + + + + + + diff --git a/src/app/components/app-job/app-job.component.html b/src/app/components/app-job/app-job.component.html index 02335030f..0cbe84b00 100644 --- a/src/app/components/app-job/app-job.component.html +++ b/src/app/components/app-job/app-job.component.html @@ -13,9 +13,9 @@
-
+ -
- ssh {{ sshd.split(':')[0] }} -p {{ sshd.split(':')[1] }} -l abstruse -
+
+
+ + + + SSH Daemon is enabled in this container. + ssh {{ sshd.split(':')[0] }} -p {{ sshd.split(':')[1] }} -l abstruse + 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 a6aaead96..8b10fb876 100644 --- a/src/app/components/app-job/app-job.component.ts +++ b/src/app/components/app-job/app-job.component.ts @@ -36,9 +36,44 @@ export class AppJobComponent implements OnInit, OnDestroy { this.loading = true; this.status = 'queued'; this.terminalOptions = { size: 'large' }; + this.id = null; } ngOnInit() { + this.termSub = this.socketService.outputEvents + .subscribe(event => { + if (event.type === 'data') { + this.ngZone.run(() => this.terminalInput = event.data); + } else if (event.type === 'jobStopped' && event.data === this.id) { + this.processing = false; + } else if (event.type === 'jobRestarted' && event.data === this.id) { + this.processing = false; + } else if (event.type === 'exposedPort') { + this.sshd = `${document.location.hostname}:${event.data}`; + } + }); + + this.sub = this.socketService.outputEvents + .filter(event => event.type === 'process') + .filter(event => event.job_id === parseInt(this.id, 10)) + .subscribe(event => { + if (!this.job) { + return; + } + + if (event.data === 'jobStarted') { + this.job.status = 'running'; + this.job.end_time = null; + this.job.start_time = new Date().getTime(); + } else if (event.data === 'jobSucceded') { + this.job.status = 'success'; + this.job.end_time = new Date().getTime(); + } else if (event.data == 'jobFailed') { + this.job.status = 'failed'; + this.job.end_time = new Date().getTime(); + } + }); + this.route.params.subscribe(params => { this.id = params.id; @@ -48,42 +83,12 @@ export class AppJobComponent implements OnInit, OnDestroy { this.timeWords = distanceInWordsToNow(job.build.start_time); this.loading = false; - this.termSub = this.socketService.outputEvents - .subscribe(event => { - if (event.type === 'data') { - this.ngZone.run(() => this.terminalInput = event.data); - } else if (event.type === 'jobStopped' && event.data === this.id) { - this.processing = false; - } else if (event.type === 'jobRestarted' && event.data === this.id) { - this.processing = false; - } else if (event.type === 'exposedPort') { - this.sshd = `${document.location.hostname}:${event.data}`; - } - }); - this.socketService.emit({ type: 'subscribeToJobOutput', data: { jobId: this.id } }); this.updateJobTime(); setInterval(() => this.updateJobTime(), 1000); - - this.sub = this.socketService.outputEvents - .filter(event => event.type === 'process') - .filter(event => event.job_id === parseInt(this.id, 10)) - .subscribe(event => { - if (event.data === 'jobStarted') { - job.status = 'running'; - job.end_time = null; - job.start_time = new Date().getTime(); - } else if (event.data === 'jobSucceded') { - job.status = 'success'; - job.end_time = new Date().getTime(); - } else if (event.data == 'jobFailed') { - job.status = 'failed'; - job.end_time = new Date().getTime(); - } - }); - }); }); + }); } ngOnDestroy() { @@ -105,6 +110,7 @@ export class AppJobComponent implements OnInit, OnDestroy { e.stopPropagation(); this.terminalInput = { clear: true }; this.processing = true; + this.sshd = null; this.socketService.emit({ type: 'restartJob', data: { jobId: this.id } }); } @@ -113,6 +119,7 @@ export class AppJobComponent implements OnInit, OnDestroy { e.stopPropagation(); this.terminalInput = { clear: true }; this.processing = true; + this.sshd = null; this.socketService.emit({ type: 'restartJobWithSSH', data: { jobId: this.id } }); } @@ -120,6 +127,7 @@ export class AppJobComponent implements OnInit, OnDestroy { e.preventDefault(); e.stopPropagation(); this.processing = true; + this.sshd = null; this.socketService.emit({ type: 'stopJob', data: { jobId: this.id } }); } diff --git a/src/app/styles/build-details.sass b/src/app/styles/build-details.sass index 7d4bb3f5f..70f18469e 100644 --- a/src/app/styles/build-details.sass +++ b/src/app/styles/build-details.sass @@ -102,3 +102,24 @@ .button margin-left: 10px + +.ssh-container + display: flex + align-items: center + padding: 15px 10px + background: $background + border: 1px solid $divider + font-size: 13px + + span + margin: 0 10px 0 0 + + .icon + margin-left: 10px + + code + background: $black + color: $white + padding: 5px 10px + border-radius: 4px + margin: 0 5px