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, private FileReaderFactory: () => FileReader) { } public extractDockerfile(file: any): Promise { return new Promise((resolve, reject) => { // TODO: Replace callbacks with promises }); } public getDockerfile(file: any, success: (dockerfile: DockerfileInfoImpl) => void, failure: (error: Event | string) => void): void { var reader: FileReader = this.FileReaderFactory(); reader.onload = (event: any) => { this.DataFileService.readDataArrayAsPossibleArchive(event.target.result, (files: any[]) => { this.processFiles(files, success, failure); }, () => { // Not an archive. Read directly as a single file. this.processFile(event.target.result, success, failure); }); }; reader.onerror = failure; reader.readAsArrayBuffer(file); } private processFile(dataArray: any, success: (dockerfile: DockerfileInfoImpl) => void, failure: (error: ErrorEvent | string) => void): void { this.DataFileService.arrayToString(dataArray, (contents: string) => { var result: DockerfileInfoImpl | null = DockerfileInfoImpl.forData(contents, this.Config); if (result == null) { failure('File chosen is not a valid Dockerfile'); } else { success(result); } }); } private processFiles(files: any[], success: (dockerfile: DockerfileInfoImpl) => void, failure: (error: ErrorEvent | string) => void): void { 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, this.Config); if (result == null) { failure('Dockerfile inside archive is not a valid Dockerfile'); } else { 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 { const 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 const lastIndex: number = imageAndTag.lastIndexOf(':'); if (lastIndex == -1) { 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). const afterColon: string = imageAndTag.substring(lastIndex + 1); if (afterColon.indexOf('/') != -1) { 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; } }