From 409ade59b785a915fae35bf6fa3c3bbb9639c4b2 Mon Sep 17 00:00:00 2001 From: Dennis Leon Date: Fri, 14 Aug 2015 17:17:33 -0700 Subject: [PATCH] compress all subsequent RUN statements into a single RUN statement Signed-off-by: Dennis Leon --- doc/changelog.md | 1 + doc/manual.md | 1 + .../maven/assembly/DockerAssemblyManager.java | 4 +++ .../maven/assembly/DockerFileBuilder.java | 30 ++++++++++++++---- .../maven/config/BuildImageConfiguration.java | 19 +++++++++++- .../config/handler/property/ConfigKey.java | 1 + .../property/PropertyConfigHandler.java | 1 + .../maven/assembly/DockerFileBuilderTest.java | 31 +++++++++++++++++++ .../property/PropertyConfigHandlerTest.java | 8 +++++ .../docker/Dockerfile_optimised.test | 10 ++++++ 10 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 src/test/resources/docker/Dockerfile_optimised.test diff --git a/doc/changelog.md b/doc/changelog.md index 08f479235..4fc138c93 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -13,6 +13,7 @@ - HTTP method and status code can be specified when waiting on an HTTP URL (#258) - Introduced global `portPropertyFile` setting (#90) - Allow the container's host ip to be bound to a maven property and exported + - Allow runCmds to be compressed into a single command. (#263) * **0.13.2** - "run" directives can be added to the Dockerfile (#191) diff --git a/doc/manual.md b/doc/manual.md index 04463ce99..cf8a66f5b 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -261,6 +261,7 @@ of an image configuration. The available subelements are which are passed to bash. The run commands are inserted right after the assembly and after **workdir** in to the Dockerfile. This tag is not to be confused with the `` section for this image which specifies the runtime behaviour when starting containers. +* **optimise** if set to true then it will compress all the runCmds into a single command. * **skip** if set to true disables building of the image. This config option is best used together with a maven property * **tags** contains a list of additional `tag` elements with which an image is to be tagged after the build. diff --git a/src/main/java/org/jolokia/docker/maven/assembly/DockerAssemblyManager.java b/src/main/java/org/jolokia/docker/maven/assembly/DockerAssemblyManager.java index 2879485b3..1d82e4104 100644 --- a/src/main/java/org/jolokia/docker/maven/assembly/DockerAssemblyManager.java +++ b/src/main/java/org/jolokia/docker/maven/assembly/DockerAssemblyManager.java @@ -207,6 +207,10 @@ DockerFileBuilder createDockerFileBuilder(BuildImageConfiguration buildConfig, A if (buildConfig.getEntryPoint() != null){ builder.entryPoint(buildConfig.getEntryPoint()); } + + if (buildConfig.optimise()) { + builder.optimise(); + } return builder; } diff --git a/src/main/java/org/jolokia/docker/maven/assembly/DockerFileBuilder.java b/src/main/java/org/jolokia/docker/maven/assembly/DockerFileBuilder.java index 705c757b0..95d21e628 100644 --- a/src/main/java/org/jolokia/docker/maven/assembly/DockerFileBuilder.java +++ b/src/main/java/org/jolokia/docker/maven/assembly/DockerFileBuilder.java @@ -8,6 +8,7 @@ import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; import org.jolokia.docker.maven.config.Arguments; + import static org.jolokia.docker.maven.assembly.DockerFileKeyword.*; /** @@ -61,6 +62,9 @@ public class DockerFileBuilder { // exposed volumes private List volumes = new ArrayList<>(); + // whether the Dockerfile should be optimised. i.e. compressing run statements into a single statement + private boolean shouldOptimise = false; + /** * Create a DockerFile in the given directory * @param destDir directory where to store the dockerfile @@ -83,10 +87,11 @@ public File write(File destDir) throws IOException { public String content() throws IllegalArgumentException { StringBuilder b = new StringBuilder(); - + FROM.addTo(b,baseImage != null ? baseImage : DockerAssemblyManager.DEFAULT_DATA_BASE_IMAGE); MAINTAINER.addTo(b,maintainer); + addOptimisation(); addEnv(b); addLabels(b); addPorts(b); @@ -128,7 +133,7 @@ private static void buildArguments(StringBuilder b, DockerFileKeyword key, Argum } key.addTo(b, arg); } - + private void addEntries(StringBuilder b) { List destinations = new ArrayList<>(); for (AddEntry entry : addEntries) { @@ -185,6 +190,14 @@ private void addPorts(StringBuilder b) { } } + public void addOptimisation() { + if (runCmds != null && !runCmds.isEmpty() && shouldOptimise) { + String optimisedRunCmd = StringUtils.join(runCmds.iterator(), " && "); + runCmds.clear(); + runCmds.add(optimisedRunCmd); + } + } + private void addRun(StringBuilder b) { for (String run : runCmds) { RUN.addTo(b,run); @@ -200,7 +213,7 @@ private void addVolumes(StringBuilder b) { addVolume(b, volume); } } - + private void addVolume(StringBuilder buffer, String volume) { while (volume.endsWith("/")) { volume = volume.substring(0, volume.length() - 1); @@ -210,7 +223,7 @@ private void addVolume(StringBuilder buffer, String volume) { VOLUME.addTo(buffer,"[\"" + volume + "\"]"); } } - + // ========================================================================== // Builder stuff .... public DockerFileBuilder() {} @@ -296,7 +309,7 @@ public DockerFileBuilder exportBasedir(Boolean exportBasedir) { this.exportBasedir = exportBasedir; return this; } - + public DockerFileBuilder env(Map values) { if (values != null) { this.envEntries.putAll(values); @@ -319,7 +332,12 @@ public DockerFileBuilder volumes(List volumes) { } return this; } - + + public DockerFileBuilder optimise() { + this.shouldOptimise = true; + return this; + } + private void validateMap(Map env) { for (Map.Entry entry : env.entrySet()) { if (entry.getValue() == null || entry.getValue().length() == 0) { diff --git a/src/main/java/org/jolokia/docker/maven/config/BuildImageConfiguration.java b/src/main/java/org/jolokia/docker/maven/config/BuildImageConfiguration.java index ee47b5a3d..f90a81a35 100644 --- a/src/main/java/org/jolokia/docker/maven/config/BuildImageConfiguration.java +++ b/src/main/java/org/jolokia/docker/maven/config/BuildImageConfiguration.java @@ -42,6 +42,11 @@ public class BuildImageConfiguration { */ private boolean cleanup = false; + /** + * @parameter default-value="false" + */ + private boolean optimise = false; + /** * @paramter */ @@ -139,6 +144,7 @@ public Arguments getCmd() { return cmd; } + @Deprecated public String getCommand() { return command; @@ -147,7 +153,11 @@ public String getCommand() { public boolean cleanup() { return cleanup; } - + + public boolean optimise() { + return optimise; + } + public boolean skip() { return skip; } @@ -237,6 +247,13 @@ public Builder cleanup(String cleanup) { return this; } + public Builder optimise(String optimise) { + if (optimise != null) { + config.optimise = Boolean.valueOf(optimise); + } + return this; + } + public Builder entryPoint(String entryPoint) { if (config.entryPoint == null) { config.entryPoint = new Arguments(); diff --git a/src/main/java/org/jolokia/docker/maven/config/handler/property/ConfigKey.java b/src/main/java/org/jolokia/docker/maven/config/handler/property/ConfigKey.java index bca945794..bac8f8ec7 100644 --- a/src/main/java/org/jolokia/docker/maven/config/handler/property/ConfigKey.java +++ b/src/main/java/org/jolokia/docker/maven/config/handler/property/ConfigKey.java @@ -36,6 +36,7 @@ public enum ConfigKey { CAP_ADD, CAP_DROP, CLEANUP, + OPTIMISE, CMD, DOMAINNAME, DNS, diff --git a/src/main/java/org/jolokia/docker/maven/config/handler/property/PropertyConfigHandler.java b/src/main/java/org/jolokia/docker/maven/config/handler/property/PropertyConfigHandler.java index 414f40fd0..b0a220129 100644 --- a/src/main/java/org/jolokia/docker/maven/config/handler/property/PropertyConfigHandler.java +++ b/src/main/java/org/jolokia/docker/maven/config/handler/property/PropertyConfigHandler.java @@ -60,6 +60,7 @@ private BuildImageConfiguration extractBuildConfiguration(String prefix, Propert return new BuildImageConfiguration.Builder() .cmd(withPrefix(prefix, CMD, properties)) .cleanup(withPrefix(prefix, CLEANUP, properties)) + .optimise(withPrefix(prefix, OPTIMISE, properties)) .entryPoint(withPrefix(prefix, ENTRYPOINT, properties)) .assembly(extractAssembly(prefix, properties)) .env(mapWithPrefix(prefix, ENV, properties)) diff --git a/src/test/java/org/jolokia/docker/maven/assembly/DockerFileBuilderTest.java b/src/test/java/org/jolokia/docker/maven/assembly/DockerFileBuilderTest.java index 01ce58621..c3f3c0179 100644 --- a/src/test/java/org/jolokia/docker/maven/assembly/DockerFileBuilderTest.java +++ b/src/test/java/org/jolokia/docker/maven/assembly/DockerFileBuilderTest.java @@ -34,12 +34,43 @@ public void testBuildDockerFile() throws Exception { assertEquals(expected, stripCR(dockerfileContent)); } + @Test + public void testDockerFileOptimisation() throws Exception { + Arguments a = Arguments.Builder.get().withParam("c1").withParam("c2").build(); + String dockerfileContent = new DockerFileBuilder().add("/src", "/dest") + .baseImage("image") + .cmd(a) + .env(ImmutableMap.of("foo", "bar")) + .basedir("/export") + .expose(Collections.singletonList("8080")) + .maintainer("maintainer@example.com") + .workdir("/tmp") + .labels(ImmutableMap.of("com.acme.foobar", "How are \"you\" ?")) + .volumes(Collections.singletonList("/vol1")) + .run(Arrays.asList("echo something", "echo second", "echo third", "echo fourth", "echo fifth")) + .optimise() + .content(); + String expected = loadFile("docker/Dockerfile_optimised.test"); + assertEquals(expected, stripCR(dockerfileContent)); + } + @Test public void testMaintainer() { String dockerfileContent = new DockerFileBuilder().maintainer("maintainer@example.com").content(); assertThat(dockerfileToMap(dockerfileContent), hasEntry("MAINTAINER", "maintainer@example.com")); } + @Test + public void testOptimise() { + String dockerfileContent = new DockerFileBuilder().optimise().run(Arrays.asList("echo something", "echo two")).content(); + assertThat(dockerfileToMap(dockerfileContent), hasEntry("RUN", "echo something && echo two")); + } + + @Test + public void testOptimiseOnEmptyRunCommandListDoesNotThrowException() { + new DockerFileBuilder().optimise().content(); + } + @Test public void testEntryPointShell() { Arguments a = Arguments.Builder.get().withShell("java -jar /my-app-1.1.1.jar server").build(); diff --git a/src/test/java/org/jolokia/docker/maven/config/handler/property/PropertyConfigHandlerTest.java b/src/test/java/org/jolokia/docker/maven/config/handler/property/PropertyConfigHandlerTest.java index 213374a94..b62a40405 100644 --- a/src/test/java/org/jolokia/docker/maven/config/handler/property/PropertyConfigHandlerTest.java +++ b/src/test/java/org/jolokia/docker/maven/config/handler/property/PropertyConfigHandlerTest.java @@ -145,6 +145,14 @@ public void testNoCleanup() throws Exception { assertEquals(false, config.getBuildConfiguration().cleanup()); } + @Test + public void testNoOptimise() throws Exception { + String[] testData = new String[] { k(NAME), "image", k(OPTIMISE), "false" }; + + ImageConfiguration config = resolveExternalImageConfig(testData); + assertEquals(false, config.getBuildConfiguration().optimise()); + } + @Test public void testNoAssembly() throws Exception { Properties props = props(k(NAME), "image"); diff --git a/src/test/resources/docker/Dockerfile_optimised.test b/src/test/resources/docker/Dockerfile_optimised.test new file mode 100644 index 000000000..8d7a339ee --- /dev/null +++ b/src/test/resources/docker/Dockerfile_optimised.test @@ -0,0 +1,10 @@ +FROM image +MAINTAINER maintainer@example.com +ENV foo=bar +LABEL com.acme.foobar="How are \"you\" ?" +EXPOSE 8080 +VOLUME ["/vol1"] +COPY /src /export/dest +WORKDIR /tmp +RUN echo something && echo second && echo third && echo fourth && echo fifth +CMD ["c1","c2"]