Permalink
Browse files

fix(job): fix job socket events

  • Loading branch information...
jkuri committed Aug 4, 2017
1 parent 5dd7698 commit 788517f126cce3fc9daab5f992a37c98d9096d1e
@@ -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<ProcessOutput> {
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<ProcessOu
exitCode = 0;
attach.write(detachKey ? detachKey : 'exit $?\r');
} else if (data.includes('EXECNOK')) {
observer.error(red(`Last executed command returned error.`));
attach.write(detachKey ? detachKey : 'exit $?\r');
} else if (!data.includes(command) && !data.includes('exit $?') &&
!data.includes('logout') && !data.includes('read escape sequence')) {
@@ -0,0 +1,7 @@
<svg width="92px" height="72px" viewBox="0 0 92 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-4.000000, 0.000000)" fill-rule="nonzero" fill="#FFFFFF">
<path d="M87.83,0.83 L12.17,0.83 C7.751722,0.83 4.17,4.411722 4.17,8.83 L4.17,63.5 C4.17,67.918278 7.751722,71.5 12.17,71.5 L87.83,71.5 C92.248278,71.5 95.83,67.918278 95.83,63.5 L95.83,8.83 C95.83,4.411722 92.248278,0.83 87.83,0.83 Z M91.83,63.5 C91.83,65.709139 90.039139,67.5 87.83,67.5 L12.17,67.5 C9.960861,67.5 8.17,65.709139 8.17,63.5 L8.17,8.83 C8.17,6.620861 9.960861,4.83 12.17,4.83 L87.83,4.83 C90.039139,4.83 91.83,6.620861 91.83,8.83 L91.83,63.5 Z M31.83,23.17 C32.2055541,23.5451365 32.4165733,24.0541815 32.4165733,24.585 C32.4165733,25.1158185 32.2055541,25.6248635 31.83,26 L23,34.83 C22.2185171,35.6114829 20.9514829,35.6114829 20.17,34.83 C19.3885171,34.0485171 19.3885171,32.7814829 20.17,32 L27.59,24.58 L20.09,17.08 C19.3085171,16.2985171 19.3085171,15.0314829 20.09,14.25 C20.8714829,13.4685171 22.1385171,13.4685171 22.92,14.25 L31.83,23.17 Z M53,33.42 C53,34.5245695 52.1045695,35.42 51,35.42 L36.5,35.42 C35.3954305,35.42 34.5,34.5245695 34.5,33.42 C34.5,32.3154305 35.3954305,31.42 36.5,31.42 L51,31.42 C52.1045695,31.42 53,32.3154305 53,33.42 Z" id="Shape"></path>
</g>
</g>
</svg>
@@ -13,9 +13,9 @@
<div class="build-top-container" [ngClass]="{ green: job?.status === 'success', yellow: job?.status === 'running', red: job?.status === 'failed' }">
<div class="build-top-content">
<div class="columns is-multiline">
<div class="column is-2">
<div class="column is-7">
<h1 class="bold">
<span><a [routerLink]="['/repo', job?.build.repository.id]">{{ job?.build.head_full_name }}</a></span>
<span><a [routerLink]="['/repo', job?.build?.repository?.id]">{{ job?.build?.head_full_name }}</a></span>
<span class="build-icon">
<img src="images/icons/clock.svg" *ngIf="job?.status === 'queued'">
<img src="images/icons/flickr.svg" *ngIf="job?.status === 'running'">
@@ -24,9 +24,6 @@ <h1 class="bold">
</span>
</h1>
</div>
<div class="column is-5">
<code *ngIf="sshd" class="ssh">ssh {{ sshd.split(':')[0] }} -p {{ sshd.split(':')[1] }} -l abstruse</code>
</div>
<div class="column is-5 justify-end right-buttons-top">
<button class="button is-fullwidth" name="btn-restart-ssh" type="button" (click)="restartJobWithSSH($event)" [disabled]="processing">
<div class="centered">
@@ -57,23 +54,23 @@ <h1 class="bold">
<hr/>

<div class="column is-3">
<h2>{{ job?.build.message }}</h2>
<h2>{{ job?.build?.message }}</h2>
</div>
<div class="column is-3">
<p>
<span class="icon">
<img src="images/icons/git-commit.svg">
</span>
<span *ngIf="job?.build.head_sha">{{ job?.build.head_sha.slice(0, 7) }}</span>
<span *ngIf="!job?.build.head_sha">{{ job?.build.sha.slice(0, 7) }}</span>
<span *ngIf="job?.build?.head_sha">{{ job?.build?.head_sha.slice(0, 7) }}</span>
<span *ngIf="!job?.build?.head_sha">{{ job?.build?.sha.slice(0, 7) }}</span>
</p>
</div>
<div class="column is-4">
<p>
<span class="icon">
<img [src]="job?.build.head_user_avatar_url" class="avatar-img">
<img [src]="job?.build?.head_user_avatar_url" class="avatar-img">
</span>
<span>{{ job?.build.author }} commited {{ timeWords }} ago</span>
<span>{{ job?.build?.author }} commited {{ timeWords }} ago</span>
</p>
</div>
<div class="column is-2">
@@ -88,6 +85,16 @@ <h2>{{ job?.build.message }}</h2>
</div>
</div>
</div>
<div class="column is-12" *ngIf="sshd">
<div class="ssh-container">
<span class="icon">
<img src="images/icons/ssh.svg">
</span>
<span>SSH Daemon is enabled in this container.</span>
<code>ssh {{ sshd.split(':')[0] }} -p {{ sshd.split(':')[1] }} -l abstruse</code>
<code>password: abstruse</code>
</div>
</div>
<div class="column is-12">
<app-terminal [data]="terminalInput" (outputData)="terminalOutput($event)" [options]="terminalOptions"></app-terminal>
</div>
@@ -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(<any>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(<any>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,13 +119,15 @@ 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 } });
}

stopJob(e: MouseEvent): void {
e.preventDefault();
e.stopPropagation();
this.processing = true;
this.sshd = null;
this.socketService.emit({ type: 'stopJob', data: { jobId: this.id } });
}

@@ -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

0 comments on commit 788517f

Please sign in to comment.