Skip to content

Commit

Permalink
Add support of docker multi stage builds (#1057)
Browse files Browse the repository at this point in the history
This PR adds support of docker multi stage builds using `Dockerfile`.
See https://docs.docker.com/develop/develop-images/multistage-build/ for feature description.
  • Loading branch information
KeZiio authored and rhuss committed Apr 6, 2019
1 parent ebb1aa4 commit 038f0df
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 19 deletions.
25 changes: 16 additions & 9 deletions src/main/java/io/fabric8/maven/docker/service/BuildService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.LinkedList;

import com.google.gson.JsonObject;
import io.fabric8.maven.docker.access.BuildOptions;
Expand Down Expand Up @@ -233,14 +234,20 @@ private void autoPullBaseImage(ImageConfiguration imageConfig, ImagePullManager
return;
}

String fromImage;
List<String> fromImages;
if (buildConfig.isDockerFileMode()) {
fromImage = extractBaseFromDockerfile(buildConfig, buildContext);
fromImages = extractBaseFromDockerfile(buildConfig, buildContext);
} else {
fromImage = extractBaseFromConfiguration(buildConfig);
fromImages = new LinkedList<>();
String baseImage = extractBaseFromConfiguration(buildConfig);
if (baseImage!=null) {
fromImages.add(extractBaseFromConfiguration(buildConfig));
}
}
if (fromImage != null && !DockerAssemblyManager.SCRATCH_IMAGE.equals(fromImage)) {
registryService.pullImageWithPolicy(fromImage, imagePullManager, buildContext.getRegistryConfig(), queryService.hasImage(fromImage));
for (String fromImage : fromImages) {
if (fromImage != null && !DockerAssemblyManager.SCRATCH_IMAGE.equals(fromImage)) {
registryService.pullImageWithPolicy(fromImage, imagePullManager, buildContext.getRegistryConfig(), queryService.hasImage(fromImage));
}
}
}

Expand All @@ -256,17 +263,17 @@ private String extractBaseFromConfiguration(BuildImageConfiguration buildConfig)
return fromImage;
}

private String extractBaseFromDockerfile(BuildImageConfiguration buildConfig, BuildContext buildContext) {
String fromImage;
private List<String> extractBaseFromDockerfile(BuildImageConfiguration buildConfig, BuildContext buildContext) {
List<String> fromImage;
try {
File fullDockerFilePath = buildConfig.getAbsoluteDockerFilePath(buildContext.getMojoParameters());
fromImage = DockerFileUtil.extractBaseImage(
fromImage = DockerFileUtil.extractBaseImages(
fullDockerFilePath,
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.
fromImage = null;
return Collections.emptyList();
}
return fromImage;
}
Expand Down
26 changes: 21 additions & 5 deletions src/main/java/io/fabric8/maven/docker/util/DockerFileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -47,16 +48,31 @@ private DockerFileUtil() {}
*
* @param dockerFile file from where to extract the base image
* @param interpolator interpolator for replacing properties
* @deprecated Use {@link DockerFileUtil#extractBaseImages} extractBaseImages instead
*/
@Deprecated
public static String extractBaseImage(File dockerFile, FixedStringSearchInterpolator interpolator) throws IOException {
List<String> result = extractBaseImages(dockerFile, interpolator);
return result.isEmpty() ? null : result.iterator().next();
}

/**
* Extract the base images from a dockerfile. All lines containing a <code>FROM</code> is
* taken.
*
* @param dockerFile file from where to extract the base image
* @param interpolator interpolator for replacing properties
* @return LinkedList of base images name or empty collection if none is found.
*/
public static List<String> extractBaseImages(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) {
return parts[1];
LinkedList<String> result = new LinkedList<>();
for (String[] fromLine : fromLines) {
if (fromLine.length > 1) {
result.add(fromLine[1]);
}
}
return null;
return result;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import java.io.File;
import java.util.Collections;

import java.util.Properties;


import io.fabric8.maven.docker.access.BuildOptions;
import io.fabric8.maven.docker.access.DockerAccess;
import io.fabric8.maven.docker.access.DockerAccessException;
import io.fabric8.maven.docker.assembly.DockerAssemblyManager;
import io.fabric8.maven.docker.config.BuildImageConfiguration;
import io.fabric8.maven.docker.util.DockerFileUtilTest;
import io.fabric8.maven.docker.config.ImageConfiguration;
import io.fabric8.maven.docker.util.Logger;
import io.fabric8.maven.docker.util.MojoParameters;
Expand All @@ -18,6 +22,7 @@
import mockit.Tested;
import mockit.Verifications;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.junit.Before;
import org.junit.Test;

Expand Down Expand Up @@ -46,6 +51,15 @@ public class BuildServiceTest {
@Mocked
private MojoParameters params;

@Mocked
Logger logger;

@Mocked
MojoParameters mojoParameters;

@Mocked
MavenProject mavenProject;

@Injectable
private QueryService queryService;

Expand Down Expand Up @@ -99,6 +113,43 @@ public void testCleanupNoExistingImage() throws Exception {
thenOldImageIsNotRemoved();
}

@Test
public void testMultiStageBuild() throws Exception {
BuildImageConfiguration buildConfig = new BuildImageConfiguration.Builder()
.cleanup("false")
.dockerFile(DockerFileUtilTest.class.getResource("Dockerfile_multi_stage").getPath())
.filter("false")
.build();

buildConfig.initAndValidate(logger);

imageConfig = new ImageConfiguration.Builder()
.name("build-image")
.alias("build-alias")
.buildConfig(buildConfig)
.build();

final ImagePullManager pullManager = new ImagePullManager(null,null, null);
final BuildService.BuildContext buildContext = new BuildService.BuildContext.Builder()
.mojoParameters(mojoParameters)
.build();

new Expectations(mojoParameters) {{
mojoParameters.getProject(); result = mavenProject;
mavenProject.getProperties(); result = new Properties();
}};

buildService.buildImage(imageConfig, pullManager, buildContext);

//verify that tries to pull both images
new Verifications() {{
queryService.hasImage("fabric8/s2i-java");
registryService.pullImageWithPolicy("fabric8/s2i-java", pullManager, buildContext.getRegistryConfig(), false);
queryService.hasImage("fabric8/s1i-java");
registryService.pullImageWithPolicy("fabric8/s1i-java", pullManager, buildContext.getRegistryConfig(), false);
}};
}

private void givenAnImageConfiguration(Boolean cleanup) {
BuildImageConfiguration buildConfig = new BuildImageConfiguration.Builder()
.cleanup(cleanup.toString())
Expand Down
17 changes: 12 additions & 5 deletions src/test/java/io/fabric8/maven/docker/util/DockerFileUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@

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 java.util.*;

import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
Expand Down Expand Up @@ -52,6 +48,17 @@ public void testSimple() throws Exception {
toTest, FixedStringSearchInterpolator.create()));
}

@Test
public void testMultiStage() throws Exception {
File toTest = copyToTempDir("Dockerfile_multi_stage");
Iterator<String> fromClauses = DockerFileUtil.extractBaseImages(
toTest, FixedStringSearchInterpolator.create()).iterator();

assertEquals("fabric8/s2i-java", fromClauses.next());
assertEquals("fabric8/s1i-java", fromClauses.next());
assertEquals(false, fromClauses.hasNext());
}

private File copyToTempDir(String resource) throws IOException {
File dir = Files.createTempDirectory("d-m-p").toFile();
File ret = new File(dir, "Dockerfile");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Dockerfile with a multi FROM clause
# see https://docs.docker.com/develop/develop-images/multistage-build/

FROM fabric8/s2i-java

FROM fabric8/s1i-java

0 comments on commit 038f0df

Please sign in to comment.