Permalink
Browse files

feat(base-image): build base image during setup process (closes #277)

  • Loading branch information...
Izak88 committed Nov 10, 2017
1 parent 6090cca commit f0fdf780d41926c405ac2bbaead895ffdac4a0a9

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -148,7 +148,6 @@
"monaco-editor": "^0.10.1",
"multer": "^1.3.0",
"ng2-datepicker": "^2.1.3",
"ngx-slimscroll": "^3.4.1",
"ngx-uploader": "^4.1.0",
"node-rsa": "^0.4.2",
"node-sass": "^4.5.3",
@@ -11,6 +11,7 @@ export interface ImageData {
name: string;
dockerfile: string;
initsh: string;
base: boolean;
}

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

export function buildAbstruseBaseImage(): void {
buildDockerImage(defaultBaseImage);
}

export function buildDockerImage(data: ImageData): void {
prepareDirectory(data).then(() => {
const folderPath = getFilePath(`images/${data.name}`);
const folderPath =
data.base ? getFilePath(`base-images/${data.name}`) : getFilePath(`images/${data.name}`);
const src = glob.sync(folderPath + '/**/*').map(filePath => filePath.split('/').pop());

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

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

export function getImages(): Promise<any> {
return new Promise((resolve, reject) => {
const imagesDir = getFilePath(`images`);
Promise.all([getImagesInDirectory('images'), getImagesInDirectory('base-images')])
.then(imgs => resolve(imgs.reduce((a, b) => a.concat(b))));
});
}


function getImagesInDirectory(path: string): Promise<any> {
return new Promise((resolve, reject) => {
const imagesDir = getFilePath(path);

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

Promise.all(imgs.map(img => {
const dockerfile = getFilePath(`images/${img.name}/Dockerfile`);
const initsh = getFilePath(`images/${img.name}/init.sh`);
const dockerfile = getFilePath(`${path}/${img.name}/Dockerfile`);
const initsh = getFilePath(`${path}/${img.name}/init.sh`);

if (fs.existsSync(dockerfile) && fs.existsSync(initsh)) {
return fs.readFile(dockerfile)
.then(dockerfileContents => {
return fs.readFile(initsh).then(initshContents => {
img.dockerfile = dockerfileContents.toString();
img.initsh = initshContents.toString();
img.base = path === 'base-images';

return img;
});
@@ -149,3 +165,47 @@ export function getImages(): Promise<any> {
});
});
}

let defaultBaseImage: ImageData = {
name: 'abstruse_builder',
dockerfile: [
'FROM ubuntu:17.10',
'',
'ENV DEBIAN_FRONTEND=noninteractive',
'',
'# please do not edit between lines or image on abstruse will not work properly',
'',
'# -------------------------------------------------------------------------------------------',
'',
'RUN set -xe \\',
' && apt-get update \\',
' && apt-get install -y --no-install-recommends ca-certificates curl build-essential \\',
' && apt-get install -y --no-install-recommends libssl-dev git python \\',
' && apt-get install -y --no-install-recommends sudo \\',
' && apt-get install -y --no-install-recommends xvfb x11vnc fluxbox xterm openssh-server',
'',
'RUN useradd -u 1000 -g 100 -G sudo --shell /bin/bash -m --home-dir /home/abstruse abstruse \\',
' && echo \'abstruse ALL=(ALL) NOPASSWD:ALL\' >> /etc/sudoers \\',
' && echo \'abstruse:abstrusePass\' | chpasswd',
'',
'COPY fluxbox /etc/init.d/',
'COPY x11vnc /etc/init.d/',
'COPY xvfb /etc/init.d/',
'COPY entry.sh /',
'',
'COPY abstruse-pty /usr/bin/abstruse-pty',
'COPY abstruse-exec.sh /usr/bin/abstruse',
'',
'USER abstruse',
'WORKDIR /home/abstruse/build',
'',
'RUN cd /home/abstruse && sudo chown -Rv 1000:100 /home/abstruse',
'',
'RUN sudo chmod +x /entry.sh /etc/init.d/* /usr/bin/abstruse*',
'CMD ["/entry.sh"]',
'',
'EXPOSE 22 5900'
].join('\n'),
initsh: '',
base: true
};
@@ -40,7 +40,7 @@ import {
import { insertEnvironmentVariable, removeEnvironmentVariable } from './db/environment-variable';
import { getLogs } from './db/log';
import { imageExists } from './docker';
import { getImages } from './image-builder';
import { getImages, buildAbstruseBaseImage } from './image-builder';
import { checkApiRequestAuth } from './security';
import {
checkConfigPresence,
@@ -770,6 +770,11 @@ export function imagesRoutes(): express.Router {
.catch(err => res.status(200).json({ status: false }));
});

router.post('/build-base', (req: express.Request, res: express.Response) => {
buildAbstruseBaseImage();
res.status(200).json({ data: true });
});

return router;
}

@@ -55,6 +55,7 @@ export function initSetup(): Promise<string> {
return makeAbstruseDir()
.then(() => makeCacheDir())
.then(() => ensureDirectory(getFilePath('images')))
.then(() => ensureDirectory(getFilePath('base-images')))
.then(() => {
const srcDir = resolve(__dirname, '../../src/files/docker-essential');
const destDir = getFilePath('docker-essential');
@@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import { NgUploaderModule } from 'ngx-uploader';
import { NgSlimScrollModule } from 'ngx-slimscroll';
import { NgDatepickerModule } from 'ng2-datepicker/ng2-datepicker';

import { AuthGuardProvider, AuthGuard } from './services/auth-guard.service';
@@ -56,7 +55,6 @@ import { ToTimePipe } from './pipes/to-time.pipe';
FormsModule,
HttpModule,
NgUploaderModule,
NgSlimScrollModule,
NgDatepickerModule
],
declarations: [
@@ -94,7 +92,6 @@ import { ToTimePipe } from './pipes/to-time.pipe';
FormsModule,
HttpModule,
NgUploaderModule,
NgSlimScrollModule,
NgDatepickerModule
]
})
@@ -14,7 +14,7 @@ <h1>Docker Build Images</h1>
<i class="ionicon ion-ios-paper-outline"></i>
Images
</button>
<button class="group-button" name="tab-build-image" [ngClass]="{ 'is-active': tab === 'build' }" (click)="tab = 'build'; resetForm();">
<button class="group-button" name="tab-build-image" [ngClass]="{ 'is-active': tab === 'build' }" (click)="tab = 'build'; resetForm(!!baseImages.length);">
<i class="ionicon ion-hammer"></i>
Build Image
</button>
@@ -36,29 +36,16 @@ <h1>Docker Build Images</h1>

<div *ngIf="tab === 'images'">
<div class="content-box image-builder-box">
<div class="columns is-multiline" *ngIf="images?.length">
<div class="column is-4" *ngFor="let i of images; let index = index;">
<div class="image-item">
<h2>{{ i.name }}</h2>
<span class="version">{{ i.version }}</span>
<img src="/images/logos/docker.svg" class="docker-img">
<span class="time">{{ i.created }}</span>
<span class="time">Created {{ i.createdAgo }} ago</span>
<span class="size">Size {{ i.size }}</span>
<button type="button" class="button is-fullwidth" (click)="editImage(index)">Edit Image</button>
</div>
</div>
</div>

<div class="columns" *ngIf="!images?.length">
<div class="columns" *ngIf="!baseImages?.length">
<div class="column is-12">
<div class="message">
<div class="columns">
<div class="column is-1">
<i class="ion ion-information"></i>
</div>
<div class="column is-11">
<p><strong>No Docker images built with Abstruse found.</strong></p>
<p><strong>No Base Images found.</strong></p>
<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>
<p>Images built using Abstruse form are later easily upgradeable as config files are stored with the application.</p>
</div>
@@ -67,21 +54,99 @@ <h2>{{ i.name }}</h2>
</div>
</div>

<div class="content-box" *ngIf="baseImages?.length">
<div class="content-box-header">
<h1>Base Images</h1>
</div>
<div class="columns is-multiline">
<div class="column is-3" *ngFor="let i of baseImages; let index = index;">
<div class="base-image-item">
<h2>{{ i.name }}</h2>
<span class="version">{{ i.version }}</span>
<img src="/images/logos/docker.svg" class="docker-img">
<span class="time">{{ i.created }}</span>
<span class="size">Size {{ i.size }}</span>
<button type="button" class="button is-small" (click)="editImage(index, true)">Edit Image</button>
</div>
</div>
</div>
</div>

<div class="content-box" *ngIf="customImages?.length">
<div class="content-box-header">
<h1>Custom Images</h1>
</div>
<div class="columns is-multiline">
<div class="column is-4" *ngFor="let i of customImages; let index = index;">
<div class="image-item">
<h2>{{ i.name }}</h2>
<span class="version">{{ i.version }}</span>
<img src="/images/logos/docker.svg" class="docker-img">
<span class="time">{{ i.created }}</span>
<span class="time">Created {{ i.createdAgo }} ago</span>
<span class="size">Size {{ i.size }}</span>
<button type="button" class="button is-fullwidth" (click)="editImage(index, false)">Edit Image</button>
</div>
</div>
</div>
</div>

</div>
</div>

<div *ngIf="tab === 'build'">
<div class="content-box image-builder-box" *ngIf="!building">
<div class="columns is-multiline">
<div class="column is-12">
<div class="content-box approve" *ngIf="approve">
<div class="columns is-multiline">
<div class="column is-12">
<h1>Warning:</h1>
</div>
<div class="column is-12">
<div class="column is-12">
Your docker file contains commands that can cause Abstruse to work incorrectly ({{dangerousCommands}}).
</div>
<div class="column is-12">
Are you sure, you want to build that image?
</div>
</div>
<div class="column is-6">
<button class="button is-fullwidth" name="btn-build" type="button" (click)="startBuild()">
<i class="ionicon ion-checkmark"></i>
<span>Build</span>
</button>
</div>
<div class="column is-6">
<button class="button is-fullwidth" name="btn-cancel" type="button" (click)="approve = false">
<i class="ionicon ion-close"></i>
<span>Cancel</span>
</button>
</div>
</div>
</div>
<h2>Image name</h2>
<input type="text" class="image-name-input" [(ngModel)]="form.name" placeholder="Image Name">
<div class="columns is-multiline" *ngIf="baseImages?.length">
<div class="column is-6">
<h2>Image Type</h2>
<app-selectbox [data]="imageTypeOptions" (ngModelChange)="changeImageTypeSelect($event)" name="imageType" [(ngModel)]="form.base"></app-selectbox>
</div>
<div class="column is-6">
<div *ngIf="!form.base">
<h2>Base Image</h2>
<app-selectbox [data]="baseImageOptions" (ngModelChange)="changeBaseImageSelect($event)" name="baseImageOptions" [(ngModel)]="baseImage"></app-selectbox>
</div>
</div>
</div>
<h2>Dockerfile</h2>
<app-editor [options]="editorOptions" [(ngModel)]="form.dockerfile" class="editor-large"></app-editor>
<h2>init.sh</h2>
<app-editor [options]="initEditorOptions" [(ngModel)]="form.initsh" class="editor-small"></app-editor>
<div *ngIf="!form.base">
<h2>init.sh</h2>
<app-editor [options]="initEditorOptions" [(ngModel)]="form.initsh" class="editor-small"></app-editor>
</div>
<div class="images-buttons-container">
<button type="button" class="button w300 green" (click)="buildImage()" name="build-image-btn">Build Image</button>
<button type="button" class="button w300 green" (click)="buildImage()" name="build-image-btn" [disabled]="approve">Build Image</button>
</div>
</div>
</div>
@@ -91,7 +156,7 @@ <h2>init.sh</h2>
<p class="has-text-centered">
Building image <strong>{{ form.name }}</strong> <span *ngIf="imageBuildsText">({{ imageBuildsText }} layers)</span> ...
</p>
<pre class="image-build-log" [innerHTML]="imageBuildLog | safeHtml" *ngIf="imageBuildLog" slimScroll [options]="scrollOptions" [scrollEvents]="scrollEvents"></pre>
<app-terminal [data]="terminalInput" [options]="terminalOptions"></app-terminal>
</div>
</div>

Oops, something went wrong.

0 comments on commit f0fdf78

Please sign in to comment.