Skip to content

Commit f0fdf78

Browse files
committed
feat(base-image): build base image during setup process (closes #277)
1 parent 6090cca commit f0fdf78

File tree

13 files changed

+424
-178
lines changed

13 files changed

+424
-178
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@
148148
"monaco-editor": "^0.10.1",
149149
"multer": "^1.3.0",
150150
"ng2-datepicker": "^2.1.3",
151-
"ngx-slimscroll": "^3.4.1",
152151
"ngx-uploader": "^4.1.0",
153152
"node-rsa": "^0.4.2",
154153
"node-sass": "^4.5.3",

src/api/image-builder.ts

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface ImageData {
1111
name: string;
1212
dockerfile: string;
1313
initsh: string;
14+
base: boolean;
1415
}
1516

1617
export interface ImageBuildOutput {
@@ -21,9 +22,14 @@ export interface ImageBuildOutput {
2122
export const imageBuilder: Subject<ImageBuildOutput> = new Subject();
2223
export const imageBuilderObs = imageBuilder.share();
2324

25+
export function buildAbstruseBaseImage(): void {
26+
buildDockerImage(defaultBaseImage);
27+
}
28+
2429
export function buildDockerImage(data: ImageData): void {
2530
prepareDirectory(data).then(() => {
26-
const folderPath = getFilePath(`images/${data.name}`);
31+
const folderPath =
32+
data.base ? getFilePath(`base-images/${data.name}`) : getFilePath(`images/${data.name}`);
2733
const src = glob.sync(folderPath + '/**/*').map(filePath => filePath.split('/').pop());
2834

2935
let msg: LogMessageType = {
@@ -75,7 +81,8 @@ export function buildDockerImage(data: ImageData): void {
7581
}
7682

7783
function prepareDirectory(data: ImageData): Promise<void> {
78-
const folderPath = getFilePath(`images/${data.name}`);
84+
const folderPath =
85+
data.base ? getFilePath(`base-images/${data.name}`) : getFilePath(`images/${data.name}`);
7986
const dockerFilePath = join(folderPath, 'Dockerfile');
8087
const initShFilePath = join(folderPath, 'init.sh');
8188
const essentialFolderPath = getFilePath(`docker-essential`);
@@ -97,7 +104,15 @@ function prepareDirectory(data: ImageData): Promise<void> {
97104

98105
export function getImages(): Promise<any> {
99106
return new Promise((resolve, reject) => {
100-
const imagesDir = getFilePath(`images`);
107+
Promise.all([getImagesInDirectory('images'), getImagesInDirectory('base-images')])
108+
.then(imgs => resolve(imgs.reduce((a, b) => a.concat(b))));
109+
});
110+
}
111+
112+
113+
function getImagesInDirectory(path: string): Promise<any> {
114+
return new Promise((resolve, reject) => {
115+
const imagesDir = getFilePath(path);
101116

102117
fs.readdir(imagesDir).then(dirs => {
103118
docker.listImages()
@@ -126,15 +141,16 @@ export function getImages(): Promise<any> {
126141
}).filter(Boolean);
127142

128143
Promise.all(imgs.map(img => {
129-
const dockerfile = getFilePath(`images/${img.name}/Dockerfile`);
130-
const initsh = getFilePath(`images/${img.name}/init.sh`);
144+
const dockerfile = getFilePath(`${path}/${img.name}/Dockerfile`);
145+
const initsh = getFilePath(`${path}/${img.name}/init.sh`);
131146

132147
if (fs.existsSync(dockerfile) && fs.existsSync(initsh)) {
133148
return fs.readFile(dockerfile)
134149
.then(dockerfileContents => {
135150
return fs.readFile(initsh).then(initshContents => {
136151
img.dockerfile = dockerfileContents.toString();
137152
img.initsh = initshContents.toString();
153+
img.base = path === 'base-images';
138154

139155
return img;
140156
});
@@ -149,3 +165,47 @@ export function getImages(): Promise<any> {
149165
});
150166
});
151167
}
168+
169+
let defaultBaseImage: ImageData = {
170+
name: 'abstruse_builder',
171+
dockerfile: [
172+
'FROM ubuntu:17.10',
173+
'',
174+
'ENV DEBIAN_FRONTEND=noninteractive',
175+
'',
176+
'# please do not edit between lines or image on abstruse will not work properly',
177+
'',
178+
'# -------------------------------------------------------------------------------------------',
179+
'',
180+
'RUN set -xe \\',
181+
' && apt-get update \\',
182+
' && apt-get install -y --no-install-recommends ca-certificates curl build-essential \\',
183+
' && apt-get install -y --no-install-recommends libssl-dev git python \\',
184+
' && apt-get install -y --no-install-recommends sudo \\',
185+
' && apt-get install -y --no-install-recommends xvfb x11vnc fluxbox xterm openssh-server',
186+
'',
187+
'RUN useradd -u 1000 -g 100 -G sudo --shell /bin/bash -m --home-dir /home/abstruse abstruse \\',
188+
' && echo \'abstruse ALL=(ALL) NOPASSWD:ALL\' >> /etc/sudoers \\',
189+
' && echo \'abstruse:abstrusePass\' | chpasswd',
190+
'',
191+
'COPY fluxbox /etc/init.d/',
192+
'COPY x11vnc /etc/init.d/',
193+
'COPY xvfb /etc/init.d/',
194+
'COPY entry.sh /',
195+
'',
196+
'COPY abstruse-pty /usr/bin/abstruse-pty',
197+
'COPY abstruse-exec.sh /usr/bin/abstruse',
198+
'',
199+
'USER abstruse',
200+
'WORKDIR /home/abstruse/build',
201+
'',
202+
'RUN cd /home/abstruse && sudo chown -Rv 1000:100 /home/abstruse',
203+
'',
204+
'RUN sudo chmod +x /entry.sh /etc/init.d/* /usr/bin/abstruse*',
205+
'CMD ["/entry.sh"]',
206+
'',
207+
'EXPOSE 22 5900'
208+
].join('\n'),
209+
initsh: '',
210+
base: true
211+
};

src/api/server-routes.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import {
4040
import { insertEnvironmentVariable, removeEnvironmentVariable } from './db/environment-variable';
4141
import { getLogs } from './db/log';
4242
import { imageExists } from './docker';
43-
import { getImages } from './image-builder';
43+
import { getImages, buildAbstruseBaseImage } from './image-builder';
4444
import { checkApiRequestAuth } from './security';
4545
import {
4646
checkConfigPresence,
@@ -770,6 +770,11 @@ export function imagesRoutes(): express.Router {
770770
.catch(err => res.status(200).json({ status: false }));
771771
});
772772

773+
router.post('/build-base', (req: express.Request, res: express.Response) => {
774+
buildAbstruseBaseImage();
775+
res.status(200).json({ data: true });
776+
});
777+
773778
return router;
774779
}
775780

src/api/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export function initSetup(): Promise<string> {
5555
return makeAbstruseDir()
5656
.then(() => makeCacheDir())
5757
.then(() => ensureDirectory(getFilePath('images')))
58+
.then(() => ensureDirectory(getFilePath('base-images')))
5859
.then(() => {
5960
const srcDir = resolve(__dirname, '../../src/files/docker-essential');
6061
const destDir = getFilePath('docker-essential');

src/app/app-routing.module.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
44
import { HttpModule } from '@angular/http';
55
import { FormsModule } from '@angular/forms';
66
import { NgUploaderModule } from 'ngx-uploader';
7-
import { NgSlimScrollModule } from 'ngx-slimscroll';
87
import { NgDatepickerModule } from 'ng2-datepicker/ng2-datepicker';
98

109
import { AuthGuardProvider, AuthGuard } from './services/auth-guard.service';
@@ -56,7 +55,6 @@ import { ToTimePipe } from './pipes/to-time.pipe';
5655
FormsModule,
5756
HttpModule,
5857
NgUploaderModule,
59-
NgSlimScrollModule,
6058
NgDatepickerModule
6159
],
6260
declarations: [
@@ -94,7 +92,6 @@ import { ToTimePipe } from './pipes/to-time.pipe';
9492
FormsModule,
9593
HttpModule,
9694
NgUploaderModule,
97-
NgSlimScrollModule,
9895
NgDatepickerModule
9996
]
10097
})

src/app/components/app-images/app-images.component.html

Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ <h1>Docker Build Images</h1>
1414
<i class="ionicon ion-ios-paper-outline"></i>
1515
Images
1616
</button>
17-
<button class="group-button" name="tab-build-image" [ngClass]="{ 'is-active': tab === 'build' }" (click)="tab = 'build'; resetForm();">
17+
<button class="group-button" name="tab-build-image" [ngClass]="{ 'is-active': tab === 'build' }" (click)="tab = 'build'; resetForm(!!baseImages.length);">
1818
<i class="ionicon ion-hammer"></i>
1919
Build Image
2020
</button>
@@ -36,29 +36,16 @@ <h1>Docker Build Images</h1>
3636

3737
<div *ngIf="tab === 'images'">
3838
<div class="content-box image-builder-box">
39-
<div class="columns is-multiline" *ngIf="images?.length">
40-
<div class="column is-4" *ngFor="let i of images; let index = index;">
41-
<div class="image-item">
42-
<h2>{{ i.name }}</h2>
43-
<span class="version">{{ i.version }}</span>
44-
<img src="/images/logos/docker.svg" class="docker-img">
45-
<span class="time">{{ i.created }}</span>
46-
<span class="time">Created {{ i.createdAgo }} ago</span>
47-
<span class="size">Size {{ i.size }}</span>
48-
<button type="button" class="button is-fullwidth" (click)="editImage(index)">Edit Image</button>
49-
</div>
50-
</div>
51-
</div>
5239

53-
<div class="columns" *ngIf="!images?.length">
40+
<div class="columns" *ngIf="!baseImages?.length">
5441
<div class="column is-12">
5542
<div class="message">
5643
<div class="columns">
5744
<div class="column is-1">
5845
<i class="ion ion-information"></i>
5946
</div>
6047
<div class="column is-11">
61-
<p><strong>No Docker images built with Abstruse found.</strong></p>
48+
<p><strong>No Base Images found.</strong></p>
6249
<p>It is important to build images using <a (click)="tab = 'build'">this</a> form so files needed for Abstruse to work are included.</p>
6350
<p>Images built using Abstruse form are later easily upgradeable as config files are stored with the application.</p>
6451
</div>
@@ -67,21 +54,99 @@ <h2>{{ i.name }}</h2>
6754
</div>
6855
</div>
6956

57+
<div class="content-box" *ngIf="baseImages?.length">
58+
<div class="content-box-header">
59+
<h1>Base Images</h1>
60+
</div>
61+
<div class="columns is-multiline">
62+
<div class="column is-3" *ngFor="let i of baseImages; let index = index;">
63+
<div class="base-image-item">
64+
<h2>{{ i.name }}</h2>
65+
<span class="version">{{ i.version }}</span>
66+
<img src="/images/logos/docker.svg" class="docker-img">
67+
<span class="time">{{ i.created }}</span>
68+
<span class="size">Size {{ i.size }}</span>
69+
<button type="button" class="button is-small" (click)="editImage(index, true)">Edit Image</button>
70+
</div>
71+
</div>
72+
</div>
73+
</div>
74+
75+
<div class="content-box" *ngIf="customImages?.length">
76+
<div class="content-box-header">
77+
<h1>Custom Images</h1>
78+
</div>
79+
<div class="columns is-multiline">
80+
<div class="column is-4" *ngFor="let i of customImages; let index = index;">
81+
<div class="image-item">
82+
<h2>{{ i.name }}</h2>
83+
<span class="version">{{ i.version }}</span>
84+
<img src="/images/logos/docker.svg" class="docker-img">
85+
<span class="time">{{ i.created }}</span>
86+
<span class="time">Created {{ i.createdAgo }} ago</span>
87+
<span class="size">Size {{ i.size }}</span>
88+
<button type="button" class="button is-fullwidth" (click)="editImage(index, false)">Edit Image</button>
89+
</div>
90+
</div>
91+
</div>
92+
</div>
93+
7094
</div>
7195
</div>
7296

7397
<div *ngIf="tab === 'build'">
7498
<div class="content-box image-builder-box" *ngIf="!building">
7599
<div class="columns is-multiline">
76100
<div class="column is-12">
101+
<div class="content-box approve" *ngIf="approve">
102+
<div class="columns is-multiline">
103+
<div class="column is-12">
104+
<h1>Warning:</h1>
105+
</div>
106+
<div class="column is-12">
107+
<div class="column is-12">
108+
Your docker file contains commands that can cause Abstruse to work incorrectly ({{dangerousCommands}}).
109+
</div>
110+
<div class="column is-12">
111+
Are you sure, you want to build that image?
112+
</div>
113+
</div>
114+
<div class="column is-6">
115+
<button class="button is-fullwidth" name="btn-build" type="button" (click)="startBuild()">
116+
<i class="ionicon ion-checkmark"></i>
117+
<span>Build</span>
118+
</button>
119+
</div>
120+
<div class="column is-6">
121+
<button class="button is-fullwidth" name="btn-cancel" type="button" (click)="approve = false">
122+
<i class="ionicon ion-close"></i>
123+
<span>Cancel</span>
124+
</button>
125+
</div>
126+
</div>
127+
</div>
77128
<h2>Image name</h2>
78129
<input type="text" class="image-name-input" [(ngModel)]="form.name" placeholder="Image Name">
130+
<div class="columns is-multiline" *ngIf="baseImages?.length">
131+
<div class="column is-6">
132+
<h2>Image Type</h2>
133+
<app-selectbox [data]="imageTypeOptions" (ngModelChange)="changeImageTypeSelect($event)" name="imageType" [(ngModel)]="form.base"></app-selectbox>
134+
</div>
135+
<div class="column is-6">
136+
<div *ngIf="!form.base">
137+
<h2>Base Image</h2>
138+
<app-selectbox [data]="baseImageOptions" (ngModelChange)="changeBaseImageSelect($event)" name="baseImageOptions" [(ngModel)]="baseImage"></app-selectbox>
139+
</div>
140+
</div>
141+
</div>
79142
<h2>Dockerfile</h2>
80143
<app-editor [options]="editorOptions" [(ngModel)]="form.dockerfile" class="editor-large"></app-editor>
81-
<h2>init.sh</h2>
82-
<app-editor [options]="initEditorOptions" [(ngModel)]="form.initsh" class="editor-small"></app-editor>
144+
<div *ngIf="!form.base">
145+
<h2>init.sh</h2>
146+
<app-editor [options]="initEditorOptions" [(ngModel)]="form.initsh" class="editor-small"></app-editor>
147+
</div>
83148
<div class="images-buttons-container">
84-
<button type="button" class="button w300 green" (click)="buildImage()" name="build-image-btn">Build Image</button>
149+
<button type="button" class="button w300 green" (click)="buildImage()" name="build-image-btn" [disabled]="approve">Build Image</button>
85150
</div>
86151
</div>
87152
</div>
@@ -91,7 +156,7 @@ <h2>init.sh</h2>
91156
<p class="has-text-centered">
92157
Building image <strong>{{ form.name }}</strong> <span *ngIf="imageBuildsText">({{ imageBuildsText }} layers)</span> ...
93158
</p>
94-
<pre class="image-build-log" [innerHTML]="imageBuildLog | safeHtml" *ngIf="imageBuildLog" slimScroll [options]="scrollOptions" [scrollEvents]="scrollEvents"></pre>
159+
<app-terminal [data]="terminalInput" [options]="terminalOptions"></app-terminal>
95160
</div>
96161
</div>
97162

0 commit comments

Comments
 (0)