com.github.dockerjava.core.dockerfile.Dockerfile Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of docker-java Show documentation
Show all versions of docker-java Show documentation
Java API Client for Docker
package com.github.dockerjava.core.dockerfile;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import com.github.dockerjava.api.exception.DockerClientException;
import com.github.dockerjava.core.GoLangFileMatch;
import com.github.dockerjava.core.exception.GoLangFileMatchException;
import com.github.dockerjava.core.util.CompressArchiveUtil;
import com.github.dockerjava.core.util.FilePathUtil;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.Collections2;
/**
* Parse a Dockerfile.
*/
public class Dockerfile {
public final File dockerFile;
private final File baseDirectory;
public Dockerfile(File dockerFile, File baseDirectory) {
if (!dockerFile.exists()) {
throw new IllegalStateException(String.format("Dockerfile %s does not exist", dockerFile.getAbsolutePath()));
}
if (!dockerFile.isFile()) {
throw new IllegalStateException(String.format("Dockerfile %s is not a file", dockerFile.getAbsolutePath()));
}
this.dockerFile = dockerFile;
if (!baseDirectory.exists()) {
throw new IllegalStateException(String.format("Base directory %s does not exist", baseDirectory.getAbsolutePath()));
}
if (!baseDirectory.isDirectory()) {
throw new IllegalStateException(String.format("Base directory %s is not a directory", baseDirectory.getAbsolutePath()));
}
this.baseDirectory = baseDirectory;
}
private static class LineTransformer implements Function> {
private int line = 0;
@Override
public Optional extends DockerfileStatement> apply(String input) {
try {
line++;
return DockerfileStatement.createFromLine(input);
} catch (Exception ex) {
throw new DockerClientException("Error on dockerfile line " + line);
}
}
}
public Iterable getStatements() throws IOException {
Collection dockerFileContent = FileUtils.readLines(dockerFile);
if (dockerFileContent.size() <= 0) {
throw new DockerClientException(String.format("Dockerfile %s is empty", dockerFile));
}
Collection> optionals = Collections2.transform(dockerFileContent,
new LineTransformer());
return Optional.presentInstances(optionals);
}
public List getIgnores() throws IOException {
List ignores = new ArrayList();
File dockerIgnoreFile = new File(baseDirectory, ".dockerignore");
if (dockerIgnoreFile.exists()) {
int lineNumber = 0;
List dockerIgnoreFileContent = FileUtils.readLines(dockerIgnoreFile);
for (String pattern : dockerIgnoreFileContent) {
lineNumber++;
pattern = pattern.trim();
if (pattern.isEmpty()) {
continue; // skip empty lines
}
pattern = FilenameUtils.normalize(pattern);
try {
ignores.add(pattern);
} catch (GoLangFileMatchException e) {
throw new DockerClientException(String.format(
"Invalid pattern '%s' on line %s in .dockerignore file", pattern, lineNumber));
}
}
}
return ignores;
}
public ScannedResult parse() throws IOException {
return new ScannedResult();
}
/**
* Result of scanning / parsing a docker file.
*/
public class ScannedResult {
final List ignores;
final List filesToAdd = new ArrayList();
public InputStream buildDockerFolderTar() {
return buildDockerFolderTar(baseDirectory);
}
public InputStream buildDockerFolderTar(File directory) {
File dockerFolderTar = null;
try {
final String archiveNameWithOutExtension = UUID.randomUUID().toString();
dockerFolderTar = CompressArchiveUtil.archiveTARFiles(directory, filesToAdd,
archiveNameWithOutExtension);
long length = dockerFolderTar.length();
final FileInputStream tarInputStream = FileUtils.openInputStream(dockerFolderTar);
final File tarFile = dockerFolderTar;
return new InputStream() {
@Override
public int available() throws IOException {
return tarInputStream.available();
}
@Override
public int read() throws IOException {
return tarInputStream.read();
}
@Override
public int read(byte[] buff, int offset, int len) throws IOException {
return tarInputStream.read(buff, offset, len);
}
@Override
public void close() throws IOException {
IOUtils.closeQuietly(tarInputStream);
FileUtils.deleteQuietly(tarFile);
}
};
} catch (IOException ex) {
FileUtils.deleteQuietly(dockerFolderTar);
throw new DockerClientException("Error occurred while preparing Docker context folder.", ex);
}
}
@Override
public String toString() {
return Objects.toStringHelper(this).add("ignores", ignores).add("filesToAdd", filesToAdd).toString();
}
public ScannedResult() throws IOException {
ignores = getIgnores();
String matchingIgnorePattern = effectiveMatchingIgnorePattern(dockerFile);
if (matchingIgnorePattern != null) {
throw new DockerClientException(String.format(
"Dockerfile is excluded by pattern '%s' in .dockerignore file", matchingIgnorePattern));
}
addFilesInDirectory(baseDirectory);
}
/**
* Adds all files found in directory
and subdirectories to
* filesToAdd
collection. It also adds any empty directories
* if found.
*
* @param directory directory
* @throws DockerClientException when IO error occurs
*/
private void addFilesInDirectory(File directory) {
File[] files = directory.listFiles();
if (files == null) {
throw new DockerClientException("Failed to read build context directory: " + baseDirectory.getAbsolutePath());
}
if (files.length != 0) {
for (File f : files) {
if (effectiveMatchingIgnorePattern(f) == null) {
if (f.isDirectory()) {
addFilesInDirectory(f);
} else {
filesToAdd.add(f);
}
}
}
// base directory should at least contains Dockerfile, but better check
} else if (!isBaseDirectory(directory)) {
// add empty directory
filesToAdd.add(directory);
}
}
private boolean isBaseDirectory(File directory) {
return directory.compareTo(baseDirectory) == 0;
}
/**
* Returns all matching ignore patterns for the given file name.
*/
private List matchingIgnorePatterns(String fileName) {
List matches = new ArrayList();
int lineNumber = 0;
for (String pattern : ignores) {
String goLangPattern = pattern.startsWith("!") ? pattern.substring(1) : pattern;
lineNumber++;
try {
if (GoLangFileMatch.match(goLangPattern, fileName)) {
matches.add(pattern);
}
} catch (GoLangFileMatchException e) {
throw new DockerClientException(String.format(
"Invalid pattern '%s' on line %s in .dockerignore file", pattern, lineNumber));
}
}
return matches;
}
/**
* Returns the matching ignore pattern for the given file or null if it should NOT be ignored. Exception rules like "!Dockerfile"
* will be respected.
*/
private String effectiveMatchingIgnorePattern(File file) {
// normalize path to replace '/' to '\' on Windows
String relativeFilename = FilenameUtils.normalize(FilePathUtil.relativize(baseDirectory, file));
List matchingPattern = matchingIgnorePatterns(relativeFilename);
if (matchingPattern.isEmpty()) {
return null;
}
String lastMatchingPattern = matchingPattern.get(matchingPattern.size() - 1);
return !lastMatchingPattern.startsWith("!") ? lastMatchingPattern : null;
}
}
}