diff --git a/src/main/java/io/fabric8/maven/docker/service/BuildService.java b/src/main/java/io/fabric8/maven/docker/service/BuildService.java index b4f0fd2d3..1b74716c7 100644 --- a/src/main/java/io/fabric8/maven/docker/service/BuildService.java +++ b/src/main/java/io/fabric8/maven/docker/service/BuildService.java @@ -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; @@ -233,14 +234,20 @@ private void autoPullBaseImage(ImageConfiguration imageConfig, ImagePullManager return; } - String fromImage; + List 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)); + } } } @@ -256,17 +263,17 @@ private String extractBaseFromConfiguration(BuildImageConfiguration buildConfig) return fromImage; } - private String extractBaseFromDockerfile(BuildImageConfiguration buildConfig, BuildContext buildContext) { - String fromImage; + private List extractBaseFromDockerfile(BuildImageConfiguration buildConfig, BuildContext buildContext) { + List 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; } diff --git a/src/main/java/io/fabric8/maven/docker/util/DockerFileUtil.java b/src/main/java/io/fabric8/maven/docker/util/DockerFileUtil.java index e222f060d..4538bb975 100644 --- a/src/main/java/io/fabric8/maven/docker/util/DockerFileUtil.java +++ b/src/main/java/io/fabric8/maven/docker/util/DockerFileUtil.java @@ -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; @@ -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 result = extractBaseImages(dockerFile, interpolator); + return result.isEmpty() ? null : result.iterator().next(); + } + + /** + * Extract the base images from a dockerfile. All lines containing a FROM 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 extractBaseImages(File dockerFile, FixedStringSearchInterpolator interpolator) throws IOException { List fromLines = extractLines(dockerFile, "FROM", interpolator); - if (!fromLines.isEmpty()) { - String[] parts = fromLines.get(0); - if (parts.length > 1) { - return parts[1]; + LinkedList result = new LinkedList<>(); + for (String[] fromLine : fromLines) { + if (fromLine.length > 1) { + result.add(fromLine[1]); } } - return null; + return result; } /** diff --git a/src/test/java/io/fabric8/maven/docker/service/BuildServiceTest.java b/src/test/java/io/fabric8/maven/docker/service/BuildServiceTest.java index 8948a2ad0..c04b1f460 100644 --- a/src/test/java/io/fabric8/maven/docker/service/BuildServiceTest.java +++ b/src/test/java/io/fabric8/maven/docker/service/BuildServiceTest.java @@ -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; @@ -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; @@ -46,6 +51,15 @@ public class BuildServiceTest { @Mocked private MojoParameters params; + @Mocked + Logger logger; + + @Mocked + MojoParameters mojoParameters; + + @Mocked + MavenProject mavenProject; + @Injectable private QueryService queryService; @@ -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()) diff --git a/src/test/java/io/fabric8/maven/docker/util/DockerFileUtilTest.java b/src/test/java/io/fabric8/maven/docker/util/DockerFileUtilTest.java index ff245c641..26b707140 100644 --- a/src/test/java/io/fabric8/maven/docker/util/DockerFileUtilTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/DockerFileUtilTest.java @@ -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; @@ -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 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"); diff --git a/src/test/resources/io/fabric8/maven/docker/util/Dockerfile_multi_stage b/src/test/resources/io/fabric8/maven/docker/util/Dockerfile_multi_stage new file mode 100644 index 000000000..368439a1f --- /dev/null +++ b/src/test/resources/io/fabric8/maven/docker/util/Dockerfile_multi_stage @@ -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 \ No newline at end of file