refactoring DockerfileService
This commit is contained in:
parent
ff6673fb07
commit
80b3666eb7
8 changed files with 326 additions and 263 deletions
|
@ -14,6 +14,7 @@ import { QuayConfig } from './quay-config.module';
|
|||
import { QuayRun } from './quay-run.module';
|
||||
import { BuildServiceImpl } from './services/build/build.service.impl';
|
||||
import { AvatarServiceImpl } from './services/avatar/avatar.service.impl';
|
||||
import { DockerfileServiceImpl } from './services/dockerfile/dockerfile.service.impl';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -37,6 +38,7 @@ import { AvatarServiceImpl } from './services/avatar/avatar.service.impl';
|
|||
ViewArrayImpl,
|
||||
BuildServiceImpl,
|
||||
AvatarServiceImpl,
|
||||
DockerfileServiceImpl,
|
||||
],
|
||||
})
|
||||
export class quay {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Service which provides helper methods for extracting information out from a Dockerfile
|
||||
* or an archive containing a Dockerfile.
|
||||
*/
|
||||
angular.module('quay').factory('DockerfileService', ['DataFileService', 'Config', function(DataFileService, Config) {
|
||||
angular.module('quay').factory('DockerfileServiceOld', ['DataFileService', 'Config', function(DataFileService, Config) {
|
||||
var dockerfileService = {};
|
||||
|
||||
function DockerfileInfo(contents) {
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import { DockerfileServiceImpl, DockerfileInfoImpl } from './dockerfile.service.impl';
|
||||
import Spy = jasmine.Spy;
|
||||
|
||||
|
||||
describe("DockerfileInfoImpl", () => {
|
||||
var dockerfileInfoImpl: DockerfileInfoImpl;
|
||||
var contents: string;
|
||||
var configMock: any;
|
||||
|
||||
beforeEach(() => {
|
||||
contents = "";
|
||||
configMock = jasmine.createSpyObj('configMock', ['getDomain']);
|
||||
dockerfileInfoImpl = new DockerfileInfoImpl(contents, configMock);
|
||||
});
|
||||
|
||||
describe("forData", () => {
|
||||
|
||||
it("returns null if given contents do not contain a 'FROM' command", () => {
|
||||
expect(DockerfileInfoImpl.forData(contents, configMock)).toBe(null);
|
||||
});
|
||||
|
||||
it("returns a new DockerfileInfoImpl instance if given contents are valid", () => {
|
||||
contents = "FROM quay.io/coreos/nginx";
|
||||
|
||||
expect(DockerfileInfoImpl.forData(contents, configMock) instanceof DockerfileInfoImpl).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRegistryBaseImage", () => {
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe("getBaseImage", () => {
|
||||
var baseImageAndTag
|
||||
|
||||
it("returns null if instance's contents do not contain a 'FROM' command", () => {
|
||||
var getBaseImageAndTagSpy: Spy = spyOn(dockerfileInfoImpl, "getBaseImageAndTag").and.returnValue()
|
||||
});
|
||||
|
||||
it("returns")
|
||||
});
|
||||
|
||||
describe("getBaseImageAndTag", () => {
|
||||
|
||||
it("returns null if instance's contents do not contain a 'FROM' command", () => {
|
||||
expect(dockerfileInfoImpl.getBaseImageAndTag()).toBe(null);
|
||||
});
|
||||
|
||||
it("returns a string containing the base image and tag from the instance's contents", () => {
|
||||
contents = "FROM quay.io/coreos/nginx";
|
||||
dockerfileInfoImpl = new DockerfileInfoImpl(contents, configMock);
|
||||
var baseImageAndTag: string = dockerfileInfoImpl.getBaseImageAndTag();
|
||||
|
||||
expect(baseImageAndTag).toEqual(contents.substring('FROM '.length, contents.length).trim());
|
||||
});
|
||||
|
||||
it("handles the presence of newlines", () => {
|
||||
contents = "FROM quay.io/coreos/nginx\nRUN echo $0";
|
||||
dockerfileInfoImpl = new DockerfileInfoImpl(contents, configMock);
|
||||
var baseImageAndTag: string = dockerfileInfoImpl.getBaseImageAndTag();
|
||||
|
||||
expect(baseImageAndTag).toEqual(contents.substring('FROM '.length, contents.indexOf('\n')).trim());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("DockerfileServiceImpl", () => {
|
||||
var dockerfileServiceImpl: DockerfileServiceImpl;
|
||||
var dataFileServiceMock: any;
|
||||
var configMock: any;
|
||||
|
||||
beforeEach(() => {
|
||||
dataFileServiceMock = jasmine.createSpyObj('dataFileServiceMock', [
|
||||
'readDataArrayAsPossibleArchive',
|
||||
'arrayToString',
|
||||
'blobToString',
|
||||
]);
|
||||
configMock = jasmine.createSpyObj('configMock', ['getDomain']);
|
||||
dockerfileServiceImpl = new DockerfileServiceImpl(dataFileServiceMock, configMock);
|
||||
});
|
||||
|
||||
describe("getDockerfile", () => {
|
||||
|
||||
});
|
||||
|
||||
describe("extractDockerfile", () => {
|
||||
|
||||
});
|
||||
});
|
147
static/js/services/dockerfile/dockerfile.service.impl.ts
Normal file
147
static/js/services/dockerfile/dockerfile.service.impl.ts
Normal file
|
@ -0,0 +1,147 @@
|
|||
import { DockerfileService, DockerfileInfo } from './dockerfile.service';
|
||||
import { Injectable } from 'angular-ts-decorators';
|
||||
|
||||
|
||||
@Injectable(DockerfileService.name)
|
||||
export class DockerfileServiceImpl implements DockerfileService {
|
||||
|
||||
constructor(private DataFileService: any, private Config: any) {
|
||||
console.log(`=================== DockerfileServiceImpl ==========================`);
|
||||
}
|
||||
|
||||
public getDockerfile(file: any,
|
||||
success: (dockerfile: DockerfileInfoImpl) => void,
|
||||
failure: (error: Event | string) => void): void {
|
||||
var reader: FileReader = new FileReader();
|
||||
reader.onload = (event: Event) => {
|
||||
var dataArray: any = reader.result;
|
||||
this.DataFileService.readDataArrayAsPossibleArchive(dataArray, (files) => {
|
||||
this.processFiles(files, dataArray, success, failure);
|
||||
}, () => {
|
||||
// Not an archive. Read directly as a single file.
|
||||
this.processFiles([], dataArray, success, failure);
|
||||
});
|
||||
};
|
||||
|
||||
reader.onerror = failure;
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
public extractDockerfile(file: any): Promise<DockerfileInfoImpl | string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// TODO: Replace callbacks with promise
|
||||
});
|
||||
}
|
||||
|
||||
private processFiles(files: any,
|
||||
dataArray: any[],
|
||||
success: (dockerfile: DockerfileInfoImpl) => void,
|
||||
failure: (error: ErrorEvent | string) => void): void {
|
||||
// The files array will be empty if the submitted file was not an archive. We therefore
|
||||
// treat it as a single Dockerfile.
|
||||
if (files.length == 0) {
|
||||
this.DataFileService.arrayToString(dataArray, (contents: string) => {
|
||||
var result = DockerfileInfoImpl.forData(contents, Object.assign({}, this.Config));
|
||||
if (!result) {
|
||||
failure('File chosen is not a valid Dockerfile');
|
||||
return;
|
||||
}
|
||||
|
||||
success(result);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var found: boolean = false;
|
||||
files.forEach((file) => {
|
||||
if (file['name'] == 'Dockerfile') {
|
||||
this.DataFileService.blobToString(file.toBlob(), (contents: string) => {
|
||||
var result: DockerfileInfoImpl | null = DockerfileInfoImpl.forData(contents, Object.assign({}, this.Config));
|
||||
if (result != null) {
|
||||
failure('Dockerfile inside archive is not a valid Dockerfile');
|
||||
return;
|
||||
}
|
||||
|
||||
success(result);
|
||||
});
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
failure('No Dockerfile found in root of archive');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class DockerfileInfoImpl implements DockerfileInfo {
|
||||
|
||||
constructor(private contents: string, private config: any) {
|
||||
|
||||
}
|
||||
|
||||
public static forData(contents: string, config: any): DockerfileInfoImpl | null {
|
||||
var dockerfileInfo: DockerfileInfoImpl = null;
|
||||
if (contents.indexOf('FROM ') != -1) {
|
||||
dockerfileInfo = new DockerfileInfoImpl(contents, config);
|
||||
}
|
||||
|
||||
return dockerfileInfo;
|
||||
}
|
||||
|
||||
public getRegistryBaseImage(): string | null {
|
||||
var baseImage = this.getBaseImage();
|
||||
if (!baseImage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (baseImage.indexOf(this.config.getDomain() + '/') != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return baseImage.substring(this.config.getDomain().length + 1);
|
||||
}
|
||||
|
||||
public getBaseImage(): string | null {
|
||||
var imageAndTag = this.getBaseImageAndTag();
|
||||
if (!imageAndTag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note, we have to handle a few different cases here:
|
||||
// 1) someimage
|
||||
// 2) someimage:tag
|
||||
// 3) host:port/someimage
|
||||
// 4) host:port/someimage:tag
|
||||
var lastIndex = imageAndTag.lastIndexOf(':');
|
||||
if (lastIndex < 0) {
|
||||
return imageAndTag;
|
||||
}
|
||||
|
||||
// Otherwise, check if there is a / in the portion after the split point. If so,
|
||||
// then the latter is part of the path (and not a tag).
|
||||
var afterColon = imageAndTag.substring(lastIndex + 1);
|
||||
if (afterColon.indexOf('/') >= 0) {
|
||||
return imageAndTag;
|
||||
}
|
||||
|
||||
return imageAndTag.substring(0, lastIndex);
|
||||
}
|
||||
|
||||
public getBaseImageAndTag(): string | null {
|
||||
var baseImageAndTag: string = null;
|
||||
|
||||
const fromIndex: number = this.contents.indexOf('FROM ');
|
||||
if (fromIndex != -1) {
|
||||
var newlineIndex: number = this.contents.indexOf('\n', fromIndex);
|
||||
if (newlineIndex == -1) {
|
||||
newlineIndex = this.contents.length;
|
||||
}
|
||||
|
||||
baseImageAndTag = this.contents.substring(fromIndex + 'FROM '.length, newlineIndex).trim();
|
||||
}
|
||||
|
||||
return baseImageAndTag;
|
||||
}
|
||||
}
|
43
static/js/services/dockerfile/dockerfile.service.ts
Normal file
43
static/js/services/dockerfile/dockerfile.service.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Service which provides helper methods for extracting information out from a Dockerfile
|
||||
* or an archive containing a Dockerfile.
|
||||
*/
|
||||
export abstract class DockerfileService {
|
||||
|
||||
/**
|
||||
* Retrieve Dockerfile from given archive file.
|
||||
* @param file File containing Dockerfile.
|
||||
* @param success Success callback with retrieved Dockerfile as parameter.
|
||||
* @param failure Failure callback with failure message as parameter.
|
||||
*/
|
||||
public abstract getDockerfile(file: any,
|
||||
success: (dockerfile: DockerfileInfo) => void,
|
||||
failure: (error: ErrorEvent | string) => void): void;
|
||||
|
||||
public abstract extractDockerfile(file: any): Promise<DockerfileInfo | string>;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Model representing information about a specific Dockerfile.
|
||||
*/
|
||||
export abstract class DockerfileInfo {
|
||||
|
||||
/**
|
||||
* Extract the registry base image from the Dockerfile contents.
|
||||
* @return registryBaseImage The registry base image.
|
||||
*/
|
||||
public abstract getRegistryBaseImage(): string | null;
|
||||
|
||||
/**
|
||||
* Extract the base image from the Dockerfile contents.
|
||||
* @return baseImage The base image.
|
||||
*/
|
||||
public abstract getBaseImage(): string | null;
|
||||
|
||||
/**
|
||||
* Extract the base image and tag from the Dockerfile contents.
|
||||
* @return baseImageAndTag The base image and tag.
|
||||
*/
|
||||
public abstract getBaseImageAndTag(): string | null;
|
||||
}
|
Reference in a new issue