Skip to content

Commit

Permalink
Support standard maven interpolation
Browse files Browse the repository at this point in the history
Replaces the regex-based property interpolation with the plexus classes
used typically by maven to perform interpolation.  This allows
references to things like ${project.build.directory} in the Dockerfile.
[fixes #871]

Signed-off-by: Scott Coplin <coplin.6@osu.edu>
  • Loading branch information
Scott Coplin authored and rhuss committed Nov 4, 2017
1 parent 35c1cf1 commit 450bb81
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 59 deletions.
6 changes: 3 additions & 3 deletions src/main/asciidoc/inc/build/_overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Except for the <<build-assembly,assembly configuration>> all other configuration
[[build-filtering]]
.Filtering
fabric8-maven-plugin filters given Dockerfile with Maven properties, much like the `maven-resource-plugin` does. Filtering is enabled by default and can be switched off with a build config `<filter>false</filter>`. Properties which we want to replace are specified with the `${..}` syntax.
Only properties which are set in the Maven build are replaced, all other remain untouched.
Replacement includes Maven project properties such as `${project.artifactId}`, properties set in the build, command-line properties, and system properties. Unresolved properties remain untouched.

This partial replacement means that you can easily mix it with Docker build arguments and environment variable reference, but you need to be careful.
If you want to be more explicit about the property delimiter to clearly separate Docker properties and Maven properties you can redefine the delimiter.
Expand All @@ -53,8 +53,8 @@ For example, the default `<filter>${*}</filter>` parse Maven properties in the f
If you specify a single character for `<filter>` then this delimiter is taken for both, the start and the end.
E.g a `<filter>@</filter>` triggers on parameters in the format `@...@`, much like in the `maven-invoker-plugin`.
Use something like this if you want to clearly separate from Docker builds args.
Property replacement works for Dockerfile only.
For replacing other data in associated files targeted for the Docker image, please use the maven-resource-plugin to copy them over before.
This form of property replacement works for Dockerfile only.
For replacing other data in other files targeted for the Docker image, please use the `maven-resource-plugin` or an <<build-assembly,assembly configuration>> with filtering to make them available in the docker build context.

.Example
The following example uses a Dockerfile in the directory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
import org.codehaus.plexus.archiver.util.DefaultFileSet;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;

import com.google.common.base.Function;

/**
* Tool for creating a docker image tar ball including a Dockerfile for building
Expand Down Expand Up @@ -111,11 +113,9 @@ public File createDockerTarArchive(String imageName, MojoParameters params, Buil
buildConfig.getDockerFile() + "\" (resolved to \"" + dockerFile + "\") doesn't exist");
}

verifyGivenDockerfile(dockerFile, buildConfig, params.getProject(), log);
Properties interpolationProperties = new Properties();
interpolationProperties.putAll(params.getProject().getProperties());
interpolationProperties.putAll(params.getSession().getSystemProperties());
interpolateDockerfile(dockerFile, buildDirs, interpolationProperties, buildConfig.getFilter());
FixedStringSearchInterpolator interpolator = DockerFileUtil.createInterpolator(params, buildConfig.getFilter());
verifyGivenDockerfile(dockerFile, buildConfig, interpolator, log);
interpolateDockerfile(dockerFile, buildDirs, interpolator);
// User dedicated Dockerfile from extra directory
customizer = new ArchiverCustomizer() {
@Override
Expand Down Expand Up @@ -168,21 +168,20 @@ private void excludeDockerfile(DefaultFileSet fileSet, File dockerFile) {
fileSet.setExcludes(excludes.toArray(new String[0]));
}

private void interpolateDockerfile(File dockerFile, BuildDirs params, Properties properties, String filter) throws IOException {
private void interpolateDockerfile(File dockerFile, BuildDirs params, FixedStringSearchInterpolator interpolator) throws IOException {
File targetDockerfile = new File(params.getOutputDirectory(), dockerFile.getName());
String dockerFileInterpolated =
DockerFileUtil.interpolate(dockerFile, properties, filter);
String dockerFileInterpolated = DockerFileUtil.interpolate(dockerFile, interpolator);
try (Writer writer = new FileWriter(targetDockerfile)) {
IOUtils.write(dockerFileInterpolated, writer);
}
}

private void verifyGivenDockerfile(File dockerFile, BuildImageConfiguration buildConfig, MavenProject project, Logger log) throws IOException {
private void verifyGivenDockerfile(File dockerFile, BuildImageConfiguration buildConfig, FixedStringSearchInterpolator interpolator, Logger log) throws IOException {
AssemblyConfiguration assemblyConfig = buildConfig.getAssemblyConfiguration();
if (assemblyConfig != null) {
String name = assemblyConfig.getName();
for (String keyword : new String[] { "ADD", "COPY" }) {
List<String[]> lines = DockerFileUtil.extractLines(dockerFile, keyword, project.getProperties(), buildConfig.getFilter());
List<String[]> lines = DockerFileUtil.extractLines(dockerFile, keyword, interpolator);
for (String[] line : lines) {
// contains an ADD/COPY ... targetDir .... All good.
if (!line[0].startsWith("#") && line.length > 1 && line[1].contains(name)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,7 @@ private String extractBaseFromDockerfile(BuildImageConfiguration buildConfig, Bu
File fullDockerFilePath = buildConfig.getAbsoluteDockerFilePath(buildContext.getMojoParameters());
fromImage = DockerFileUtil.extractBaseImage(
fullDockerFilePath,
buildContext.getMojoParameters().getProject().getProperties(),
buildConfig.getFilter());
DockerFileUtil.createInterpolator(buildContext.getMojoParameters(), buildConfig.getFilter()));
} catch (IOException e) {
// Cant extract base image, so we wont try an auto pull. An error will occur later anyway when
// building the image, so we are passive here.
Expand Down
66 changes: 36 additions & 30 deletions src/main/java/io/fabric8/maven/docker/util/DockerFileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.maven.plugin.assembly.interpolation.AssemblyInterpolator;
import org.apache.maven.plugin.assembly.io.DefaultAssemblyReader;
import org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;

import io.fabric8.maven.docker.assembly.DockerAssemblyConfigurationSource;


/**
* Utility class for dealing with dockerfiles
Expand All @@ -37,11 +42,11 @@ private DockerFileUtil() {}
* taken.
*
* @param dockerFile file from where to extract the base image
* @param properties holding values used for interpolation
*@param filter @return the base image name or <code>null</code> if none is found.
* @param interpolator interpolator for replacing properties
* @param filter @return the base image name or <code>null</code> if none is found.
*/
public static String extractBaseImage(File dockerFile, Properties properties, String filter) throws IOException {
List<String[]> fromLines = extractLines(dockerFile, "FROM", properties, filter);
public static String extractBaseImage(File dockerFile, FixedStringSearchInterpolator interpolator) throws IOException {
List<String[]> fromLines = extractLines(dockerFile, "FROM", interpolator);
if (!fromLines.isEmpty()) {
String[] parts = fromLines.get(0);
if (parts.length > 1) {
Expand All @@ -56,15 +61,15 @@ public static String extractBaseImage(File dockerFile, Properties properties, St
*
* @param dockerFile dockerfile to examine
* @param keyword keyword to extract the lines for
* @param interpolator interpolator for replacing properties
* @return list of matched lines or an empty list
*/
public static List<String[]> extractLines(File dockerFile, String keyword, Properties props, String filter) throws IOException {
public static List<String[]> extractLines(File dockerFile, String keyword, FixedStringSearchInterpolator interpolator) throws IOException {
List<String[]> ret = new ArrayList<>();
String[] delimiters = extractDelimiters(filter);
try (BufferedReader reader = new BufferedReader(new FileReader(dockerFile))) {
String line;
while ((line = reader.readLine()) != null) {
String lineInterpolated = interpolateLine(line, props, delimiters);
String lineInterpolated = interpolator.interpolate(line);
String[] lineParts = lineInterpolated.split("\\s+");
if (lineParts.length > 0 && lineParts[0].equalsIgnoreCase(keyword)) {
ret.add(lineParts);
Expand All @@ -78,40 +83,41 @@ public static List<String[]> extractLines(File dockerFile, String keyword, Prope
* Interpolate a docker file with the given properties and filter
*
* @param dockerFile docker file to interpolate
* @param properties properties to replace
* @param filter filter holding delimeters
* @return
* @param interpolator interpolator for replacing properties
* @return The interpolated contents of the file.
* @throws IOException
*/
public static String interpolate(File dockerFile, Properties properties, String filter) throws IOException {
public static String interpolate(File dockerFile, FixedStringSearchInterpolator interpolator) throws IOException {
StringBuilder ret = new StringBuilder();
String[] delimiters = extractDelimiters(filter);
try (BufferedReader reader = new BufferedReader(new FileReader(dockerFile))) {
String line;
while ((line = reader.readLine()) != null) {
ret.append(interpolateLine(line, properties, delimiters)).append(System.lineSeparator());
ret.append(interpolator.interpolate(line)).append(System.lineSeparator());
}
}
return ret.toString();
}

private static String interpolateLine(String line, Properties properties, String[] delimiters) {
if (delimiters == null || delimiters.length == 0) {
return line;
}
Pattern propertyPattern =
Pattern.compile("(?<variable>" + Pattern.quote(delimiters[0]) + "(?<prop>.*?)" + Pattern.quote(delimiters[1]) + ")");
Matcher matcher = propertyPattern.matcher(line);
StringBuffer ret = new StringBuffer();
while (matcher.find()) {
String prop = matcher.group("prop");
String value = properties.containsKey(prop) ?
properties.getProperty(prop) :
matcher.group("variable");
matcher.appendReplacement(ret, value.replace("$","\\$"));
/**
* Create an interpolator for the given maven parameters and filter configuration.
*
* @param params The maven parameters.
* @param filter The filter configuration.
* @return An interpolator for replacing maven properties.
*/
public static FixedStringSearchInterpolator createInterpolator(MojoParameters params, String filter) {
String[] delimiters = extractDelimiters(filter);
if (delimiters == null) {
// Don't interpolate anything
return FixedStringSearchInterpolator.create();
}
matcher.appendTail(ret);
return ret.toString();

DockerAssemblyConfigurationSource configSource = new DockerAssemblyConfigurationSource(params, null, null);
// Patterned after org.apache.maven.plugin.assembly.interpolation.AssemblyExpressionEvaluator
return AssemblyInterpolator
.fullInterpolator(params.getProject(),
DefaultAssemblyReader.createProjectInterpolator(params.getProject()), configSource)
.withExpressionMarkers(delimiters[0], delimiters[1]);
}

private static String[] extractDelimiters(String filter) {
Expand Down
49 changes: 39 additions & 10 deletions src/test/java/io/fabric8/maven/docker/util/DockerFileUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,24 @@

import java.io.*;
import java.nio.file.Files;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Settings;
import org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;
import org.codehaus.plexus.util.IOUtil;
import org.junit.Test;

import mockit.Mock;
import mockit.MockUp;

import static io.fabric8.maven.docker.util.PathTestUtil.createTmpFile;
import static org.junit.Assert.assertEquals;

Expand All @@ -38,7 +48,7 @@ public class DockerFileUtilTest {
public void testSimple() throws Exception {
File toTest = copyToTempDir("Dockerfile_from_simple");
assertEquals("fabric8/s2i-java", DockerFileUtil.extractBaseImage(
toTest, null, "false"));
toTest, FixedStringSearchInterpolator.create()));
}

private File copyToTempDir(String resource) throws IOException {
Expand All @@ -52,12 +62,7 @@ private File copyToTempDir(String resource) throws IOException {

@Test
public void interpolate() throws Exception {
Properties props = new Properties();
props.put("base", "java");
props.put("name", "guenther");
props.put("age", "42");
props.put("ext", "png");

MojoParameters params = mockMojoParams();
Map<String, String> filterMapping = new HashMap<>();
filterMapping.put("none", "false");
filterMapping.put("var", "${*}");
Expand All @@ -68,9 +73,10 @@ public void interpolate() throws Exception {
File dockerFile = getDockerfilePath(i, entry.getKey());
File expectedDockerFile = new File(dockerFile.getParent(), dockerFile.getName() + ".expected");
File actualDockerFile = createTmpFile(dockerFile.getName());
FileUtils.write(actualDockerFile,
DockerFileUtil.interpolate(dockerFile, props, entry.getValue()), "UTF-8");
FileUtils.contentEqualsIgnoreEOL(expectedDockerFile, actualDockerFile, "UTF-8");
FixedStringSearchInterpolator interpolator = DockerFileUtil.createInterpolator(params, entry.getValue());
FileUtils.write(actualDockerFile, DockerFileUtil.interpolate(dockerFile, interpolator), "UTF-8");
// Compare text lines without regard to EOL delimiters
assertEquals(FileUtils.readLines(expectedDockerFile), FileUtils.readLines(actualDockerFile));
}
}
}
Expand All @@ -81,4 +87,27 @@ private File getDockerfilePath(int i, String dir) {
String.format("interpolate/%s/Dockerfile_%d", dir, i)).getFile());
}

private MojoParameters mockMojoParams() {
MavenProject project = new MavenProject();
project.setArtifactId("docker-maven-plugin");

Properties projectProperties = project.getProperties();
projectProperties.put("base", "java");
projectProperties.put("name", "guenther");
projectProperties.put("age", "42");
projectProperties.put("ext", "png");

Settings settings = new Settings();
ArtifactRepository localRepository = new MockUp<ArtifactRepository>() {
@Mock
public String getBasedir() {
return "repository";
}
}.getMockInstance();
@SuppressWarnings("deprecation")
MavenSession session = new MavenSession(null, settings, localRepository, null, null, Collections.<String>emptyList(), ".", null, null, new Date(System.currentTimeMillis()));
session.getUserProperties().setProperty("cliOverride", "cliValue"); // Maven CLI override: -DcliOverride=cliValue
session.getSystemProperties().put("user.name", "somebody"); // Java system property: -Duser.name=somebody
return new MojoParameters(session, project, null, null, null, settings, "src", "target", Collections.singletonList(project));
}
}
2 changes: 1 addition & 1 deletion src/test/resources/interpolate/at/Dockerfile_1
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM @base@
LABEL name ${name} @age@ ${ext} blub @unknown@ @
LABEL name ${name} @age@ ${ext} blub @unknown@ @project.artifactId@ @
ENV @name@ @ext@
2 changes: 1 addition & 1 deletion src/test/resources/interpolate/at/Dockerfile_1.expected
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM java
LABEL name ${name} 42 ${ext} blub @unknown@ @
LABEL name ${name} 42 ${ext} blub @unknown@ docker-maven-plugin @
ENV guenther png
4 changes: 3 additions & 1 deletion src/test/resources/interpolate/var/Dockerfile_1
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
FROM ${base}
LABEL name ${name} @age@ ${ext} ${unknown}
LABEL name ${name} @age@ ${ext} ${unknown} ${project.artifactId}
ENV ${name} ${age}
ENV cli=${cliOverride}
USER ${user.name}
4 changes: 3 additions & 1 deletion src/test/resources/interpolate/var/Dockerfile_1.expected
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
FROM java
LABEL name guenther @age@ png ${unknown}
LABEL name guenther @age@ png ${unknown} docker-maven-plugin
ENV guenther 42
ENV cli=cliValue
USER somebody

0 comments on commit 450bb81

Please sign in to comment.