X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static File archiveTARFiles(File base, Iterable<File> files, String archi
return tarFile;
}

private static String relativize(File base, File absolute) {
public static String relativize(File base, File absolute) {
String relative = base.toURI().relativize(absolute.toURI()).getPath();
return relative;
}
Expand Down
257 changes: 257 additions & 0 deletions src/main/java/com/github/dockerjava/core/GoLangFileMatch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/**
* Copyright (C) 2014 SignalFuse, Inc.
*/
package com.github.dockerjava.core;

import java.io.File;
import java.util.List;

import org.apache.commons.lang.StringUtils;

/**
* Implementation of golang's file.Match
*
* Match returns true if name matches the shell file name pattern. The pattern syntax is:
*
* <pre>
* pattern:
* { term }
* term:
* '*' matches any sequence of non-Separator characters
* '?' matches any single non-Separator character
* '[' [ '^' ] { character-range } ']'
* character class (must be non-empty)
* c matches character c (c != '*', '?', '\\', '[')
* '\\' c matches character c
*
* character-range:
* c matches character c (c != '\\', '-', ']')
* '\\' c matches character c
* lo '-' hi matches character c for lo <= c <= hi
*
* Match requires pattern to match all of name, not just a substring.
* The only possible returned error is ErrBadPattern, when pattern
* is malformed.
*
* On Windows, escaping is disabled. Instead, '\\' is treated as
* path separator.
* </pre>
*
* @author tedo
*
*/
public class GoLangFileMatch {

public static final boolean IS_WINDOWS = File.separatorChar == '\\';

public static boolean match(List<String> patterns, File file) {
return match(patterns, file.getPath());
}

public static boolean match(String pattern, File file) {
return match(pattern, file.getPath());
}

public static boolean match(List<String> patterns, String name) {
for (String pattern : patterns) {
if (match(pattern, name)) {
return true;
}
}
return false;
}

public static boolean match(String pattern, String name) {
Pattern: while (!pattern.isEmpty()) {
ScanResult scanResult = scanChunk(pattern);
pattern = scanResult.pattern;
if (scanResult.star && StringUtils.isEmpty(scanResult.chunk)) {
// Trailing * matches rest of string unless it has a /.
return name.indexOf(File.separatorChar) < 0;
}
// Look for match at current position.
String matchResult = matchChunk(scanResult.chunk, name);

// if we're the last chunk, make sure we've exhausted the name
// otherwise we'll give a false result even if we could still match
// using the star
if (matchResult != null && (matchResult.isEmpty() || !pattern.isEmpty())) {
name = matchResult;
continue;
}
if (scanResult.star) {
for (int i = 0; i < name.length() && name.charAt(i) != File.separatorChar; i++) {
matchResult = matchChunk(scanResult.chunk, name.substring(i + 1));
if (matchResult != null) {
// if we're the last chunk, make sure we exhausted the name
if (pattern.isEmpty() && !matchResult.isEmpty()) {
continue;
}
name = matchResult;
continue Pattern;
}
}
}
return false;
}
return name.isEmpty();
}

static ScanResult scanChunk(String pattern) {
boolean star = false;
if (!pattern.isEmpty() && pattern.charAt(0) == '*') {
pattern = pattern.substring(1);
star = true;
}
boolean inRange = false;
int i;
Scan: for (i = 0; i < pattern.length(); i++) {
switch (pattern.charAt(i)) {
case '\\': {
if (!IS_WINDOWS) {
// error check handled in matchChunk: bad pattern.
if (i + 1 < pattern.length()) {
i++;
}
}
break;
}
case '[':
inRange = true;
break;
case ']':
inRange = false;
break;
case '*':
if (!inRange) {
break Scan;
}
}
}
return new ScanResult(star, pattern.substring(0, i), pattern.substring(i));
}

static String matchChunk(String chunk, String s) {
int chunkLength = chunk.length();
int chunkOffset = 0;
int sLength = s.length();
int sOffset = 0;
char r;
while (chunkOffset < chunkLength) {
if (sOffset == sLength) {
return null;
}
switch (chunk.charAt(chunkOffset)) {
case '[':
r = s.charAt(sOffset);
sOffset++;
chunkOffset++;
// We can't end right after '[', we're expecting at least
// a closing bracket and possibly a caret.
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
}
// possibly negated
boolean negated = chunk.charAt(chunkOffset) == '^';
if (negated) {
chunkOffset++;
}
// parse all ranges
boolean match = false;
int nrange = 0;
while (true) {
if (chunkOffset < chunkLength && chunk.charAt(chunkOffset) == ']' && nrange > 0) {
chunkOffset++;
break;
}
GetEscResult result = getEsc(chunk, chunkOffset, chunkLength);
char lo = result.lo;
char hi = lo;
chunkOffset = result.chunkOffset;
if (chunk.charAt(chunkOffset) == '-') {
result = getEsc(chunk, ++chunkOffset, chunkLength);
chunkOffset = result.chunkOffset;
hi = result.lo;
}
if (lo <= r && r <= hi) {
match = true;
}
nrange++;
}
if (match == negated) {
return null;
}
break;

case '?':
if (s.charAt(sOffset) == File.separatorChar) {
return null;
}
sOffset++;
chunkOffset++;
break;
case '\\':
if (!IS_WINDOWS) {
chunkOffset++;
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
}
}
// fallthrough
default:
if (chunk.charAt(chunkOffset) != s.charAt(sOffset)) {
return null;
}
sOffset++;
chunkOffset++;
}
}
return s.substring(sOffset);
}

static GetEscResult getEsc(String chunk, int chunkOffset, int chunkLength) {
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
}
char r = chunk.charAt(chunkOffset);
if (r == '-' || r == ']') {
throw new GoLangFileMatchException();
}
if (r == '\\' && !IS_WINDOWS) {
chunkOffset++;
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
}

}
r = chunk.charAt(chunkOffset);
chunkOffset++;
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
}
return new GetEscResult(r, chunkOffset);
}

private static final class ScanResult {
public boolean star;
public String chunk;
public String pattern;

public ScanResult(boolean star, String chunk, String pattern) {
this.star = star;
this.chunk = chunk;
this.pattern = pattern;
}
}

private static final class GetEscResult {
public char lo;
public int chunkOffset;

public GetEscResult(char lo, int chunkOffset) {
this.lo = lo;
this.chunkOffset = chunkOffset;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright (C) 2014 SignalFuse, Inc.
*/
package com.github.dockerjava.core;

public class GoLangFileMatchException extends IllegalArgumentException {

private static final long serialVersionUID = -1204971075600864898L;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (C) 2014 SignalFuse, Inc.
*/
package com.github.dockerjava.core;

import java.io.File;
import java.util.List;

import org.apache.commons.io.filefilter.AbstractFileFilter;

public class GoLangMatchFileFilter extends AbstractFileFilter {

private final List<String> patterns;


public GoLangMatchFileFilter(List<String> patterns) {
super();
this.patterns = patterns;
}

@Override
public boolean accept(File file) {
return !GoLangFileMatch.match(patterns, file);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;

import com.github.dockerjava.api.DockerClientException;
import com.github.dockerjava.api.command.BuildImageCmd;
import com.github.dockerjava.core.CompressArchiveUtil;

import com.github.dockerjava.core.GoLangFileMatch;
import com.github.dockerjava.core.GoLangFileMatchException;
import com.github.dockerjava.core.GoLangMatchFileFilter;
import com.google.common.base.Preconditions;

/**
Expand Down Expand Up @@ -158,6 +162,30 @@ protected InputStream buildDockerFolderTar(File dockerFolder) {
"Dockerfile %s is empty", dockerFile));
}

List<String> ignores = new ArrayList<String>();
File dockerIgnoreFile = new File(dockerFolder, ".dockerignore");
if (dockerIgnoreFile.exists()) {
int lineNumber = 0;
List<String> dockerIgnoreFileContent = FileUtils.readLines(dockerIgnoreFile);
for (String pattern: dockerIgnoreFileContent) {
lineNumber++;
pattern = pattern.trim();
if (pattern.isEmpty()) {
continue; // skip empty lines
}
pattern = FilenameUtils.normalize(pattern);
try {
// validate pattern and make sure we aren't excluding Dockerfile
if (GoLangFileMatch.match(pattern, "Dockerfile")) {
throw new DockerClientException(
String.format("Dockerfile is excluded by pattern '%s' on line %s in .dockerignore file", pattern, lineNumber));
}
ignores.add(pattern);
} catch (GoLangFileMatchException e) {
throw new DockerClientException(String.format("Invalid pattern '%s' on line %s in .dockerignore file", pattern, lineNumber));
}
}
}
List<File> filesToAdd = new ArrayList<File>();
filesToAdd.add(dockerFile);

Expand Down Expand Up @@ -215,10 +243,13 @@ protected InputStream buildDockerFolderTar(File dockerFolder) {
"Source file %s doesn't exist", src));
}
if (src.isDirectory()) {
filesToAdd.addAll(FileUtils.listFiles(src, null,
true));
} else {
filesToAdd.addAll(FileUtils.listFiles(src,
new GoLangMatchFileFilter(ignores), TrueFileFilter.INSTANCE));
} else if (!GoLangFileMatch.match(ignores, CompressArchiveUtil.relativize(dockerFolder, src))){
filesToAdd.add(src);
} else {
throw new DockerClientException(String.format(
"Source file %s is excluded by .dockerignore file", src));
}
}
}
Expand Down
Loading
X Tutup