From 9ed41938a617580cb854443c99d7cf5e24661dc7 Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 14 Oct 2016 13:21:44 +0100 Subject: [PATCH] New `circleAwareLogDirectory` enables partial log capture on CI --- README.md | 8 ++++ build.gradle | 1 + .../docker/compose/DockerComposeRule.java | 9 ++++ .../docker/compose/logging/LogDirectory.java | 34 +++++++++++++++ .../compose/logging/LogDirectoryTest.java | 41 +++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 src/main/java/com/palantir/docker/compose/logging/LogDirectory.java create mode 100644 src/test/java/com/palantir/docker/compose/logging/LogDirectoryTest.java diff --git a/README.md b/README.md index 24a476d9d..e514449cb 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,14 @@ public class DockerComposeRuleTest { This will automatically record logs for all containers in real time to the specified directory. Collection will stop when the containers terminate. +The `LogDirectory` class contains utility methods to generate these paths. For example, you can write logs directly into the `$CIRCLE_ARTIFACTS` directory on CI (but fall back to `build/dockerLogs` locally) using: + +```java + .saveLogsTo(circleAwareLogDirectory(MyTest.class)) +``` + +Methods in `LogDirectory` are intended to be statically imported for readability. + Skipping shutdown ----------------- diff --git a/build.gradle b/build.gradle index 2f77b19a9..5cf472dac 100644 --- a/build.gradle +++ b/build.gradle @@ -71,6 +71,7 @@ dependencies { testCompile "org.mockito:mockito-core:$mockitoVersion" testCompile "com.github.tomakehurst:wiremock:2.0.6-beta" testCompile "com.google.code.findbugs:jsr305:3.0.0" + testCompile "com.github.stefanbirkner:system-rules:1.16.1" } // Instead of copying files out of `$buildDir/test-results/` on CircleCI, we can diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index fc4865a61..34fa3a1eb 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -32,6 +32,7 @@ import com.palantir.docker.compose.logging.DoNothingLogCollector; import com.palantir.docker.compose.logging.FileLogCollector; import com.palantir.docker.compose.logging.LogCollector; +import com.palantir.docker.compose.logging.LogDirectory; import java.io.IOException; import java.util.List; import org.immutables.value.Value; @@ -169,6 +170,14 @@ public Builder file(String dockerComposeYmlFile) { return files(DockerComposeFiles.from(dockerComposeYmlFile)); } + /** + * Save the output of docker logs to files, stored in the path directory. + * + * See {@link LogDirectory} for some useful utilities, for example: + * {@link LogDirectory#circleAwareLogDirectory}. + * + * @param path directory into which log files should be saved + */ public Builder saveLogsTo(String path) { return logCollector(FileLogCollector.fromPath(path)); } diff --git a/src/main/java/com/palantir/docker/compose/logging/LogDirectory.java b/src/main/java/com/palantir/docker/compose/logging/LogDirectory.java new file mode 100644 index 000000000..7187fc101 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/logging/LogDirectory.java @@ -0,0 +1,34 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.logging; + +import java.util.Optional; + +public class LogDirectory { + + private LogDirectory() {} + + /** + * For tests running on CircleCI, save logs into $CIRCLE_ARTIFACTS/dockerLogs/<testClassName>. + * This ensures partial logs can be recovered if the build is cancelled or times out, and + * also avoids needless copying. + * + * Otherwise, save logs from local runs to a folder inside $project/build/dockerLogs named + * after the test class. + * + * @param testClass the JUnit test class whose name will appear on the log folder + */ + public static String circleAwareLogDirectory(Class testClass) { + String artifactRoot = Optional.ofNullable(System.getenv("CIRCLE_ARTIFACTS")).orElse("build"); + return artifactRoot + "/dockerLogs/" + testClass.getSimpleName(); + } + + /** + * Save logs into a new folder, $project/build/dockerLogs/<testClassName>. + */ + public static String gradleDockerLogsDirectory(Class testClass) { + return "build/dockerLogs/" + testClass.getSimpleName(); + } +} diff --git a/src/test/java/com/palantir/docker/compose/logging/LogDirectoryTest.java b/src/test/java/com/palantir/docker/compose/logging/LogDirectoryTest.java new file mode 100644 index 000000000..34969ce98 --- /dev/null +++ b/src/test/java/com/palantir/docker/compose/logging/LogDirectoryTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.logging; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; + +public class LogDirectoryTest { + + @Rule + public final EnvironmentVariables variablesRule = new EnvironmentVariables(); + + @Test + public void gradleDockerLogsDirectory_should_use_class_simple_name() { + String directory = LogDirectory.gradleDockerLogsDirectory(SomeTestClass.class); + assertThat(directory, is("build/dockerLogs/SomeTestClass")); + } + + @Test + public void circleAwareLogDirectory_should_match_gradleDockerLogsDirectory_by_default() { + variablesRule.set("CIRCLE_ARTIFACTS", null); + String directory = LogDirectory.circleAwareLogDirectory(SomeTestClass.class); + assertThat(directory, is("build/dockerLogs/SomeTestClass")); + } + + @Test + public void circleAwareLogDirectory_should_use_circle_environment_variable_if_available() { + variablesRule.set("CIRCLE_ARTIFACTS", "/tmp/circle-artifacts.g4DjuuD"); + + String directory = LogDirectory.circleAwareLogDirectory(SomeTestClass.class); + assertThat(directory, is("/tmp/circle-artifacts.g4DjuuD/dockerLogs/SomeTestClass")); + } + + private static class SomeTestClass {} +}