Skip to content
This repository was archived by the owner on Jul 15, 2022. It is now read-only.

Commit 7460503

Browse files
feat: added jobs
* chore(jobs): created basic jobs logic * chore: added job details * chore: added VSCode attach to renderer process launch config * chore: changed npm scripts to support VSCode debugging * refactor(jobs): moved job event binding from job runner to job instance * feat: pull images on background * feat: show background jobs on the sidebar * refactor(jobs): improvements on pull image job's performance * chore: removed dead code * refactor: renamed job details to job logs * refactor: added basic logis for child jobs * chore: improvements on child job logs * chore: added job ids using guids * chore: create containers as jobs * chore: fixed application launch with jobs * chore: improvements on sidenav jobs * chore: added job logs * chore: application update job, missing cancellation * chore: settings now use the update job * chore: fixed update job not completing
1 parent 7c4311b commit 7460503

File tree

60 files changed

+1446
-279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1446
-279
lines changed

Diff for: .vscode/launch.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [{
7+
"name": "Attach to Renderer",
8+
"type": "chrome",
9+
"request": "attach",
10+
"port": 9222,
11+
"webRoot": "${workspaceFolder}/src",
12+
"sourceMaps": true,
13+
"sourceMapPathOverrides": {
14+
"*": "${webRoot}/*"
15+
}
16+
}]
17+
}

Diff for: package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
"gen-ver": "node tim-version-gen",
1818
"gen-changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
1919
"gen-releasenotes": "conventional-changelog -p angular -i release-notes.md -s",
20-
"start": "yarn postinstall:electron && yarn gen-ver && npm-run-all -p ng:serve electron:serve",
20+
"start": "yarn postinstall:electron && yarn gen-ver && npm-run-all -p ng:serve electron:serve ",
2121
"build": "yarn postinstall:electron && yarn gen-ver && yarn electron:tsc && ng build",
2222
"build:dev": "yarn build -c dev",
2323
"build:prod": "yarn gen-releasenotes && yarn build -c production",
2424
"ng:serve": "ng serve --configuration hmr",
2525
"ng:serve:web": "yarn postinstall:web && ng serve -o",
2626
"electron:tsc": "tsc main.ts --lib es2017,es2016,es2015,dom",
27-
"electron:serve": "wait-on http-get://localhost:4200/ && yarn electron:tsc && electron . --serve",
27+
"electron:serve": "wait-on http-get://localhost:4200/ && yarn electron:tsc && electron . --serve --remote-debugging-port=9222",
2828
"electron:local": "yarn build:prod && electron .",
2929
"electron:linux": "yarn build:prod && npx electron-builder build --linux",
3030
"electron:windows": "yarn build:prod && npx electron-builder build --windows",
@@ -38,6 +38,7 @@
3838
"bump-version": "node bump-version && yarn gen-changelog"
3939
},
4040
"dependencies": {
41+
"@types/uuid": "^3.4.3",
4142
"electron-settings": "^3.2.0",
4243
"electron-updater": "^3.1.1"
4344
},
@@ -97,6 +98,7 @@
9798
"split-ca": "^1.0.1",
9899
"tslint": "^5.11.0",
99100
"typescript": "^2.9.2",
101+
"uuid": "^3.3.2",
100102
"wait-on": "2.1.0",
101103
"webdriver-manager": "^12.1.0",
102104
"xterm": "^3.6.0",

Diff for: src/app/app-tabs.module.ts

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { ApplicationTemplatesModule } from './application-templates/application-
2424
import { RegistryModule } from './registry/registry.module';
2525
import { SettingsModule } from './settings/settings.module';
2626
import { TabConfiguration } from './tabs/tab.model';
27+
import { ContainerLauncherComponent } from './daemon-tools/container-launcher/container-launcher.component';
2728

2829
const TIMONEER_AVAILABLE_TABS: TabConfiguration[] = [
2930
{
@@ -54,6 +55,11 @@ const TIMONEER_AVAILABLE_TABS: TabConfiguration[] = [
5455
title: 'New Container',
5556
component: ContainerCreateContainerComponent,
5657
},
58+
{
59+
id: TimoneerTabs.DOCKER_CONTAINER_LAUNCHER,
60+
title: 'Launching Container',
61+
component: ContainerLauncherComponent,
62+
},
5763
{
5864
id: TimoneerTabs.DOCKER_VOLUMES,
5965
title: 'Volumes',

Diff for: src/app/app.module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import { HotkeyModule } from 'angular2-hotkeys';
77
import { AppTabsModule } from './app-tabs.module';
88
import { GlobalShortcutsModule } from './global-shortcuts.module';
99
import { AppMenuModule } from './app-menu/app-menu.module';
10+
import { JobsModule } from './jobs/jobs.module';
1011

1112
@NgModule({
1213
imports: [
1314
BrowserModule,
1415
BrowserAnimationsModule,
1516
HotkeyModule.forRoot(),
17+
JobsModule.forRoot(),
1618

1719
AppTabsModule,
1820
AppMenuModule,

Diff for: src/app/application-templates/application-launch/application-launch.component.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { Component, Input } from '@angular/core';
22
import { Application } from '../application.model';
33
import { TabService } from '../../tabs/tab.service';
44
import { TimoneerTabs } from '../../timoneer-tabs';
5+
import { JobInstance } from '../../jobs/job-instance';
6+
import { ContainerCreationJob } from '../../daemon-tools/container-creation-job';
7+
import { ContainerLauncherParams } from '../../daemon-tools/container-launcher/container-launcher.component';
58

69
@Component({
710
selector: 'tim-application-launch',
@@ -15,10 +18,11 @@ export class ApplicationLaunchComponent {
1518

1619
constructor(private tabService: TabService) { }
1720

18-
public containerCreated(id: string) {
19-
this.tabService.replaceCurrent(TimoneerTabs.DOCKER_ATTACH, {
20-
title: `Attached to ${id.slice(0, 12)}`,
21-
params: id,
21+
public containerCreated(job: JobInstance<ContainerCreationJob, string>) {
22+
this.tabService.replaceCurrent(TimoneerTabs.DOCKER_CONTAINER_LAUNCHER, {
23+
params: {
24+
jobId: job.id,
25+
} as ContainerLauncherParams,
2226
});
2327
}
2428
}

Diff for: src/app/daemon-tools/container-create-container/container-create-container.component.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { Component, OnInit, Inject } from '@angular/core';
22
import { TAB_DATA } from '../../tabs/tab.model';
33
import { TabService } from '../../tabs/tab.service';
44
import { TimoneerTabs } from '../../timoneer-tabs';
5+
import { ContainerCreationJob } from '../container-creation-job';
6+
import { JobInstance } from '../../jobs/job-instance';
7+
import { ContainerLauncherParams } from '../container-launcher/container-launcher.component';
58

69
@Component({
710
selector: 'tim-container-create-container',
@@ -18,10 +21,11 @@ export class ContainerCreateContainerComponent implements OnInit {
1821
public ngOnInit() {
1922
}
2023

21-
public containerCreated(id: string) {
22-
this.tabService.add(TimoneerTabs.DOCKER_ATTACH, {
23-
title: `Attached to ${id.slice(0, 12)}`,
24-
params: id,
24+
public containerCreated(job: JobInstance<ContainerCreationJob, string>) {
25+
this.tabService.replaceCurrent(TimoneerTabs.DOCKER_CONTAINER_LAUNCHER, {
26+
params: {
27+
jobId: job.id,
28+
} as ContainerLauncherParams,
2529
});
2630
}
2731
}

Diff for: src/app/daemon-tools/container-create/container-create.component.ts

+8-16
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
22
import { FormBuilder, Validators, FormArray } from '@angular/forms';
3-
import { switchMap } from 'rxjs/operators';
4-
import { NotificationService } from '../../shared/notification.service';
5-
import { ImageInspectInfo, Container, ContainerCreateBody } from 'dockerode';
6-
import { from } from 'rxjs';
7-
import { DockerContainerService } from '../docker-container.service';
3+
import { ImageInspectInfo, ContainerCreateBody } from 'dockerode';
84
import {
95
ContainerCreationSuggestedPort, PortBinding,
106
ContainerCreationSuggestedVolume, VolumeBinding, VolumeBindingType
117
} from '../docker-client.model';
8+
import { DockerJobsService } from '../docker-jobs.service';
9+
import { JobInstance } from '../../jobs/job-instance';
10+
import { ContainerCreationJob } from '../container-creation-job';
1211

1312
@Component({
1413
selector: 'tim-container-create',
@@ -30,7 +29,7 @@ export class ContainerCreateComponent implements OnInit {
3029
public suggestedVolumes: ContainerCreationSuggestedVolume[];
3130

3231
@Output()
33-
public created = new EventEmitter<string>();
32+
public created = new EventEmitter<JobInstance<ContainerCreationJob, string>>();
3433

3534
public form = this.fb.group({
3635
'image': ['', Validators.required],
@@ -53,8 +52,7 @@ export class ContainerCreateComponent implements OnInit {
5352

5453
public imageData: ImageInspectInfo;
5554

56-
constructor(private containerService: DockerContainerService,
57-
private notification: NotificationService,
55+
constructor(private dockerJobs: DockerJobsService,
5856
private fb: FormBuilder) {
5957
}
6058

@@ -107,13 +105,7 @@ export class ContainerCreateComponent implements OnInit {
107105

108106
data.name = launchConfig.containerName;
109107

110-
this.containerService.create(data)
111-
.pipe(switchMap(container => {
112-
this.notification.open(`Container Created. Starting.. ${container.id}`);
113-
return from(container.start() as Promise<Container>);
114-
}))
115-
.subscribe(container => {
116-
this.created.emit(container.id);
117-
});
108+
const job = this.dockerJobs.createContainer(data);
109+
this.created.emit(job);
118110
}
119111
}

Diff for: src/app/daemon-tools/container-creation-job.ts

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { takeUntil, map, catchError, switchMap } from 'rxjs/operators';
2+
import { JobDefinition } from '../jobs/job-definition';
3+
import { Job } from '../jobs/job.decorator';
4+
import { ContainerCreateBody, Container } from 'dockerode';
5+
import { DockerContainerService } from './docker-container.service';
6+
import { PullImageJobParams, PullImageJob } from './pull-image.job';
7+
import { DockerImageService } from './docker-image.service';
8+
import { throwError, from } from 'rxjs';
9+
10+
export class ContainerCreationJobParams {
11+
constructor(public readonly creationData: ContainerCreateBody) { }
12+
}
13+
14+
@Job()
15+
export class ContainerCreationJob extends JobDefinition<string> {
16+
public get title() {
17+
if (this.creationData.name) {
18+
return `Create ${this.creationData.name}`;
19+
} else {
20+
return `Create from ${this.creationData.Image}`;
21+
}
22+
}
23+
24+
protected get creationData() {
25+
return this.params.creationData;
26+
}
27+
28+
constructor(protected params: ContainerCreationJobParams,
29+
protected imageService: DockerImageService,
30+
protected containerService: DockerContainerService) {
31+
super();
32+
}
33+
34+
public start() {
35+
this.pullImage()
36+
.pipe(switchMap(() => {
37+
this.progressAndLog({
38+
percent: 75,
39+
message: 'Creating container'
40+
});
41+
return this.containerService.create(this.creationData)
42+
.pipe(
43+
takeUntil(this.cancelled),
44+
switchMap(container => {
45+
this.progressAndLog({
46+
percent: 80,
47+
message: `Starting container ${container.id}`
48+
});
49+
return from(container.start() as Promise<Container>);
50+
})
51+
);
52+
}))
53+
.subscribe(container => {
54+
this.progressAndLog({
55+
percent: 100,
56+
message: `Container started`
57+
});
58+
59+
this.complete(container.id);
60+
}, e => {
61+
this.completeWithError(e);
62+
});
63+
64+
}
65+
66+
protected pullImage() {
67+
const image = this.creationData.Image;
68+
this.progressAndLog({
69+
percent: 0,
70+
message: `Inspecting image ${image}`
71+
});
72+
return this.imageService.inspectImage(image)
73+
.pipe(
74+
map(imageInfo => {
75+
76+
this.progressAndLog({
77+
percent: 50,
78+
message: `Obtained image metadata ${image}`
79+
});
80+
81+
return imageInfo;
82+
}),
83+
catchError((error: { statusCode: number, message: string }) => {
84+
85+
if (error.statusCode === 404) {
86+
this.progressAndLog({
87+
percent: 25,
88+
message: `Image ${image} not found in daemon`
89+
});
90+
const job = this.startPullImageJob();
91+
return job.completed
92+
.pipe(switchMap(() => {
93+
this.progressAndLog({
94+
percent: 50,
95+
message: `Obtained Image ${image}`
96+
});
97+
return this.imageService.inspectImage(image);
98+
}));
99+
} else {
100+
return throwError(error);
101+
}
102+
}),
103+
);
104+
105+
}
106+
107+
protected startPullImageJob() {
108+
const params = new PullImageJobParams(this.creationData.Image);
109+
return this.startChildJob(PullImageJob, {
110+
provide: PullImageJobParams,
111+
useValue: params
112+
});
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div fxLayout="column"
2+
style="padding: 16px; height:100%">
3+
<tim-job-logs *ngIf="job"
4+
[job]="job"></tim-job-logs>
5+
</div>

Diff for: src/app/daemon-tools/container-launcher/container-launcher.component.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
2+
import { TAB_DATA } from '../../tabs/tab.model';
3+
import { JobInstance } from '../../jobs/job-instance';
4+
import { ContainerCreationJob } from '../container-creation-job';
5+
import { TimoneerTabs } from '../../timoneer-tabs';
6+
import { TabService } from '../../tabs/tab.service';
7+
import { JobRunnerService } from '../../jobs/job-runner.service';
8+
import { Subject } from 'rxjs';
9+
10+
export interface ContainerLauncherParams {
11+
jobId: string;
12+
}
13+
14+
@Component({
15+
selector: 'tim-container-launcher',
16+
templateUrl: './container-launcher.component.html',
17+
styleUrls: ['./container-launcher.component.scss']
18+
})
19+
export class ContainerLauncherComponent implements OnInit, OnDestroy {
20+
21+
public job: JobInstance<ContainerCreationJob, string>;
22+
23+
private componentDestroyed = new Subject<void>();
24+
25+
constructor(
26+
private tabService: TabService,
27+
private jobRunner: JobRunnerService,
28+
@Inject(TAB_DATA)
29+
private params: ContainerLauncherParams
30+
) {
31+
}
32+
33+
public ngOnInit() {
34+
35+
this.job = this.jobRunner.getJobById(this.params.jobId);
36+
if (this.job) {
37+
this.job.completed
38+
.subscribe(containerId => {
39+
this.tabService.replaceCurrent(TimoneerTabs.DOCKER_ATTACH, {
40+
title: `Attached to ${containerId.slice(0, 12)}`,
41+
params: containerId,
42+
});
43+
});
44+
} else {
45+
console.error('failed to find job with id ' + this.params.jobId);
46+
}
47+
}
48+
49+
public ngOnDestroy() {
50+
this.componentDestroyed.next();
51+
this.componentDestroyed.unsubscribe();
52+
}
53+
}

Diff for: src/app/daemon-tools/daemon-modal.service.ts

-19
This file was deleted.

0 commit comments

Comments
 (0)