Skip to content
This repository was archived by the owner on Mar 21, 2022. It is now read-only.

Issue 185 : Allow one to log process messages to a file #186

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
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
49 changes: 47 additions & 2 deletions src/main/java/com/spotify/docker/BuildMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.spotify.docker.client.AnsiProgressHandler;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.DockerException;
import com.spotify.docker.client.ProgressHandler;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigFactory;
Expand All @@ -52,6 +53,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -92,6 +94,12 @@ public class BuildMojo extends AbstractDockerMojo {
*/
private static final char WINDOWS_SEPARATOR = '\\';

/**
* File to log output
*/
@Parameter(property = "logOutput")
private String logOutput;

/**
* Directory containing the Dockerfile. If the value is not set, the plugin will generate a
* Dockerfile using the required baseImage value, plus the optional entryPoint, cmd and maintainer
Expand Down Expand Up @@ -328,7 +336,11 @@ protected void execute(final DockerClient docker)
copyResources(destination);
}

buildImage(docker, destination, buildParams());
if (logOutput == null) {
buildImage(docker, destination, buildParams());
} else {
buildImage(docker, destination, new File(logOutput), buildParams());
}
tagImage(docker, forceTags);

final DockerBuildInformation buildInfo = new DockerBuildInformation(imageName, getLog());
Expand Down Expand Up @@ -540,12 +552,45 @@ private void validateParameters() throws MojoExecutionException {
}

private void buildImage(final DockerClient docker, final String buildDir,
final ProgressHandler progressHandler,
final DockerClient.BuildParam... buildParams)
throws MojoExecutionException, DockerException, IOException, InterruptedException {
getLog().info("Building image " + imageName);
docker.build(Paths.get(buildDir), imageName, new AnsiProgressHandler(), buildParams);
docker.build(Paths.get(buildDir), imageName, progressHandler, buildParams);
getLog().info("Built " + imageName);
}

private void buildImage(final DockerClient docker, final String buildDir,
final File output,
final DockerClient.BuildParam... buildParams)
throws MojoExecutionException, DockerException, IOException, InterruptedException {

if (output.isDirectory() || (output.exists() && !output.canWrite())) {
throw new MojoExecutionException("The specified output file is a directory or cannot "
+ "be written");
}

File parent = output.getParentFile();
if (parent.isFile()) {
throw new MojoExecutionException("The specified output file's parent is a file");
}

if (!parent.exists() && !parent.mkdirs()) {
throw new MojoExecutionException("The specified output file's parent cannot be made a"
+ " directory");
}

try (PrintStream printStream =
new PrintStream(new FileOutputStream(output, true), true, "UTF-8")) {
buildImage(docker, buildDir, new AnsiProgressHandler(printStream), buildParams);
}
}

private void buildImage(final DockerClient docker, final String buildDir,
final DockerClient.BuildParam... buildParams)
throws MojoExecutionException, DockerException, IOException, InterruptedException {
buildImage(docker, buildDir, new AnsiProgressHandler(), buildParams);
}

private void tagImage(final DockerClient docker, boolean forceTags)
throws DockerException, InterruptedException, MojoExecutionException {
Expand Down
111 changes: 111 additions & 0 deletions src/test/java/com/spotify/docker/BuildMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@
import com.spotify.docker.client.AnsiProgressHandler;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.DockerClient.BuildParam;
import com.spotify.docker.client.DockerException;
import com.spotify.docker.client.ProgressHandler;
import com.spotify.docker.client.messages.ProgressMessage;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.testing.AbstractMojoTestCase;
import org.apache.maven.project.MavenProject;
import org.mockito.ArgumentMatcher;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

Expand All @@ -56,10 +59,12 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class BuildMojoTest extends AbstractMojoTestCase {

Expand Down Expand Up @@ -448,6 +453,112 @@ public void testNoCache() throws Exception {
eq(BuildParam.noCache()));
}

public void testLogOutputToFileButParentIsFile() throws Exception {
testLogOutputToFileButFileCannotBeWritten(false);
}

public void testLogOutputToFileButFileIsDirectory() throws Exception {
testLogOutputToFileButFileCannotBeWritten(true);
}

public void testLogOutputToFileButFileCannotBeWritten(boolean dir) throws Exception {
final File pom = getTestFile("src/test/resources/pom-build-log-output.xml");
assertNotNull("Null pom.xml", pom);
assertTrue("pom.xml does not exist", pom.exists());

// Make sure initially the file to be logged does not exist
final String outputFileName = "target/docker/outputDir/file-to-log-output.log";
final File outputFile = getTestFile(outputFileName);
assertNotNull("Null output file", outputFile);
assertFalse("output file already exists", outputFile.exists());

if (dir) {
// Force it being a directory
assertTrue("Cannot create directory ", outputFile.mkdirs());
} else {
// Force parent is be a file
File parent = outputFile.getParentFile();
parent.getParentFile().mkdirs();
assertTrue("Cannot create parent file ", parent.createNewFile());
}

final BuildMojo mojo = setupMojo(pom);
final DockerClient docker = mock(DockerClient.class);
try {
mojo.execute(docker);
fail("mojo should have thrown exception because output file cannot be written to");
} catch (MojoExecutionException e) {
final String message;
if (dir) {
message = "The specified output file is a directory or cannot be written";
} else {
message = "The specified output file's parent is a file";
}
assertTrue(String.format("Exception message should have contained '%s'", message),
e.getMessage().contains(message));
}
}

public void testLogOutputToNewFile() throws Exception {
testLogOutputToFile(true);
}

public void testLogOutputToExistingFile() throws Exception {
testLogOutputToFile(false);
}

private void testLogOutputToFile(boolean newFile) throws Exception {
final File pom = getTestFile("src/test/resources/pom-build-log-output.xml");
assertNotNull("Null pom.xml", pom);
assertTrue("pom.xml does not exist", pom.exists());

// Make sure initially the file to be logged does not exist
final String outputFileName = "target/docker/outputDir/file-to-log-output.log";
final File outputFile = getTestFile(outputFileName);
assertNotNull("Null output file", outputFile);
assertFalse("output file already exists", outputFile.exists());

final BuildMojo mojo = setupMojo(pom);
final DockerClient docker = mock(DockerClient.class);

// A matcher that grabs the instantiated AnsiProgressHandler and logs a message
final String testMessage = "Testing progress is logged to file";
ArgumentMatcher<AnsiProgressHandler> matcher = new ArgumentMatcher<AnsiProgressHandler>() {

@Override
public boolean matches(Object argument) {
assertTrue(AnsiProgressHandler.class.isInstance(argument));
AnsiProgressHandler handler = AnsiProgressHandler.class.cast(argument);
ProgressMessage message = new ProgressMessage();
message.status(testMessage);
try {
handler.progress(message);
} catch (DockerException e) {
fail("Unexpected error");
}
return true;
}

};

if (! newFile) {
File parent = outputFile.getParentFile();
assertTrue("Cannot create parent directory", parent.exists() || parent.mkdirs());
assertTrue("Cannot create output file", outputFile.createNewFile());
}

when(docker.build(eq(Paths.get("target/docker")), eq("busybox"), argThat(matcher))).thenReturn(StringUtils.EMPTY);

mojo.execute(docker);

verify(docker).build(eq(Paths.get("target/docker")), eq("busybox"), any(AnsiProgressHandler.class));

// Make sure output file exists and message is logged
assertFileExists(outputFileName);
byte[] encoded = Files.readAllBytes(Paths.get(outputFileName));
assertEquals(testMessage + System.lineSeparator(), new String(encoded, "UTF-8"));
}

private BuildMojo setupMojo(final File pom) throws Exception {
final MavenProject project = new ProjectStub(pom);
final MavenSession session = newMavenSession(project);
Expand Down
31 changes: 31 additions & 0 deletions src/test/resources/pom-build-log-output.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<name>Docker Maven Plugin Test Pom</name>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.1-SNAPSHOT</version>
<configuration>
<!-- we have to supply values for all params because unit testing framework -->
<!-- doesn't set default values even though the are set in mojo-->
<dockerHost>http://host:2375</dockerHost>
<dockerDirectory>src/test/resources/dockerDirectory</dockerDirectory>
<imageName>busybox</imageName>
<!-- test base image is pulled -->
<logOutput>target/docker/outputDir/file-to-log-output.log</logOutput>
</configuration>
</plugin>
</plugins>
</build>
</project>