From 195d9aff4a4fbd358c3882303165edfd72a81a6c Mon Sep 17 00:00:00 2001 From: Sam Wright Date: Wed, 1 Mar 2017 15:04:26 +0000 Subject: [PATCH 01/20] Locate docker and docker-compose using the path variable. This searches through the path variable's directories for the docker commands, instead of looking in predetermined OS-specific locations or requiring new environmental variables to be set. This also renames DockerCommandLocations to DockerCommandLocator, which better reflects what it is. --- .baseline/findbugs/excludeFilter.xml | 2 +- .../execution/DockerCommandLocations.java | 47 ------- .../execution/DockerCommandLocator.java | 68 +++++++++++ .../execution/DockerComposeExecutable.java | 12 +- .../compose/execution/DockerExecutable.java | 12 +- .../DockerCommandLocationsShould.java | 70 ----------- .../execution/DockerCommandLocatorShould.java | 115 ++++++++++++++++++ 7 files changed, 186 insertions(+), 140 deletions(-) delete mode 100644 docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java create mode 100644 docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java delete mode 100644 docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java create mode 100644 docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java diff --git a/.baseline/findbugs/excludeFilter.xml b/.baseline/findbugs/excludeFilter.xml index 1887223f2..47b8bfda0 100644 --- a/.baseline/findbugs/excludeFilter.xml +++ b/.baseline/findbugs/excludeFilter.xml @@ -9,7 +9,7 @@ - + diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java deleted file mode 100644 index a1e0c0a06..000000000 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.docker.compose.execution; - -import static java.util.Arrays.asList; - -import java.io.File; -import java.util.List; -import java.util.Optional; -import java.util.function.Predicate; - -public class DockerCommandLocations { - private static final Predicate IS_NOT_NULL = path -> path != null; - private static final Predicate FILE_EXISTS = path -> new File(path).exists(); - - private final List possiblePaths; - - public DockerCommandLocations(String... possiblePaths) { - this.possiblePaths = asList(possiblePaths); - } - - public Optional preferredLocation() { - - return possiblePaths.stream() - .filter(IS_NOT_NULL) - .filter(FILE_EXISTS) - .findFirst(); - } - - @Override - public String toString() { - return "DockerCommandLocations{possiblePaths=" + possiblePaths + "}"; - } -} diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java new file mode 100644 index 000000000..832720826 --- /dev/null +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.docker.compose.execution; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.SystemUtils; + +public class DockerCommandLocator { + private static final Pattern PATH_SPLITTER = Pattern.compile(File.pathSeparator); + + private final String command; + + public DockerCommandLocator(String command) { + this.command = command; + } + + public String getLocation() { + // Get path variable, ignoring the case of its name + String path = getEnv().entrySet().stream() + .filter(e -> e.getKey().equalsIgnoreCase("path")) + .findFirst() + .map(Map.Entry::getValue) + .orElseThrow(() -> new IllegalStateException("Could not find path variable in env")); + + // The filename is the same as the command, except on Windows where it ends with ".exe" + String filename = isWindows() ? command + ".exe" : command; + + // Look through the directories in path for the given command which must exist and be executable + return PATH_SPLITTER.splitAsStream(path) + .map(p -> Paths.get(p, filename)) + .filter(Files::exists) + .findFirst() + .map(Path::toString) + .orElseThrow(() -> new IllegalStateException("Could not find " + command + " in path")); + } + + protected Map getEnv() { + return System.getenv(); + } + + protected boolean isWindows() { + return SystemUtils.IS_OS_WINDOWS; + } + + @Override + public String toString() { + return "DockerCommandLocator{command=" + command + "}"; + } +} diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecutable.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecutable.java index 9c2daed21..15927abd0 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecutable.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecutable.java @@ -29,19 +29,9 @@ public abstract class DockerComposeExecutable implements Executable { private static final Logger log = LoggerFactory.getLogger(DockerComposeExecutable.class); - private static final DockerCommandLocations DOCKER_COMPOSE_LOCATIONS = new DockerCommandLocations( - System.getenv("DOCKER_COMPOSE_LOCATION"), - "/usr/local/bin/docker-compose", - "/usr/bin/docker-compose" - ); - private static String defaultDockerComposePath() { - String pathToUse = DOCKER_COMPOSE_LOCATIONS.preferredLocation() - .orElseThrow(() -> new IllegalStateException( - "Could not find docker-compose, looked in: " + DOCKER_COMPOSE_LOCATIONS)); - + String pathToUse = new DockerCommandLocator("docker-compose").getLocation(); log.debug("Using docker-compose found at " + pathToUse); - return pathToUse; } diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java index 1ff5cf01e..ede18a051 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java @@ -26,12 +26,6 @@ public abstract class DockerExecutable implements Executable { private static final Logger log = LoggerFactory.getLogger(DockerExecutable.class); - private static final DockerCommandLocations DOCKER_LOCATIONS = new DockerCommandLocations( - System.getenv("DOCKER_LOCATION"), - "/usr/local/bin/docker", - "/usr/bin/docker" - ); - @Value.Parameter protected abstract DockerConfiguration dockerConfiguration(); @Override @@ -41,12 +35,8 @@ public final String commandName() { @Value.Derived protected String dockerPath() { - String pathToUse = DOCKER_LOCATIONS.preferredLocation() - .orElseThrow(() -> new IllegalStateException( - "Could not find docker, looked in: " + DOCKER_LOCATIONS)); - + String pathToUse = new DockerCommandLocator("docker").getLocation(); log.debug("Using docker found at " + pathToUse); - return pathToUse; } diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java deleted file mode 100644 index f5b2e44eb..000000000 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.docker.compose.execution; - -import static java.util.Optional.empty; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; - -import java.io.IOException; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -public class DockerCommandLocationsShould { - private static final String badLocation = "file/that/does/not/exist"; - private static final String otherBadLocation = "another/file/that/does/not/exist"; - - @Rule public TemporaryFolder folder = new TemporaryFolder(); - - private String goodLocation; - - @Before - public void setup() throws IOException { - goodLocation = folder.newFile("docker-compose.yml").getAbsolutePath(); - } - - @Test public void - provide_the_first_docker_command_location_if_it_exists() { - DockerCommandLocations dockerCommandLocations = new DockerCommandLocations( - badLocation, - goodLocation, - otherBadLocation); - - assertThat(dockerCommandLocations.preferredLocation().get(), - is(goodLocation)); - } - - @Test public void - skip_paths_from_environment_variables_that_are_unset() { - DockerCommandLocations dockerCommandLocations = new DockerCommandLocations( - System.getenv("AN_UNSET_DOCKER_COMPOSE_PATH"), - goodLocation); - - assertThat(dockerCommandLocations.preferredLocation().get(), - is(goodLocation)); - } - - @Test public void - have_no_preferred_path_when_all_possible_paths_are_all_invalid() { - DockerCommandLocations dockerCommandLocations = new DockerCommandLocations( - badLocation); - - assertThat(dockerCommandLocations.preferredLocation(), - is(empty())); - } -} diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java new file mode 100644 index 000000000..d0322d21f --- /dev/null +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java @@ -0,0 +1,115 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.docker.compose.execution; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +public class DockerCommandLocatorShould { + private static final String command = "command"; + private static final String windowsCommand = command + ".exe"; + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Rule public ExpectedException exception = ExpectedException.none(); + + private final DockerCommandLocator locator = spy(new DockerCommandLocator(command)); + + private final Map env = new HashMap<>(); + + private Path emptyFolder; + + private Path firstFolder; + + private Path secondFolder; + + private String commandFile; + + private String windowsCommandFile; + + private String pathString; + + @Before + public void setup() throws IOException { + emptyFolder = folder.newFolder("empty").toPath(); + firstFolder = folder.newFolder("first").toPath(); + secondFolder = folder.newFolder("second").toPath(); + + commandFile = Files.createFile(firstFolder.resolve(command)).toString(); + windowsCommandFile = Files.createFile(firstFolder.resolve(windowsCommand)).toString(); + Files.createFile(secondFolder.resolve(command)); + Files.createFile(secondFolder.resolve(windowsCommand)); + + pathString = Stream.of(emptyFolder, firstFolder, secondFolder) + .map(Path::toString) + .collect(Collectors.joining(File.pathSeparator)); + + doReturn(env).when(locator).getEnv(); + } + + @Test public void + provide_the_first_command_location() { + env.put("path", pathString); + doReturn(false).when(locator).isWindows(); + assertThat(locator.getLocation(), is(commandFile)); + } + + @Test public void + provide_the_first_command_location_using_capitalised_path() { + env.put("PATH", pathString); + doReturn(false).when(locator).isWindows(); + assertThat(locator.getLocation(), is(commandFile)); + } + + @Test public void + provide_the_first_command_location_on_windows() { + env.put("path", pathString); + doReturn(true).when(locator).isWindows(); + assertThat(locator.getLocation(), is(windowsCommandFile)); + } + + @Test public void + fail_when_no_paths_contain_command() { + env.put("path", emptyFolder.toString()); + exception.expect(IllegalStateException.class); + exception.expectMessage("Could not find " + command + " in path"); + locator.getLocation(); + } + + @Test public void + fail_when_no_path_variable_is_set() { + exception.expect(IllegalStateException.class); + exception.expectMessage("Could not find path variable in env"); + locator.getLocation(); + } +} From a12076d96114ecb69b6ca6a0b7ff6a2beb9ccfbe Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Tue, 7 Mar 2017 14:20:31 +0000 Subject: [PATCH 02/20] Reintroduce support for explicitly setting docker-compose location. --- .../execution/DockerCommandLocator.java | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java index 832720826..8ba2da174 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java @@ -19,50 +19,51 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Map; +import java.util.Optional; import java.util.regex.Pattern; - +import java.util.stream.Stream; import org.apache.commons.lang3.SystemUtils; +import org.immutables.value.Value; -public class DockerCommandLocator { +@Value.Immutable +public abstract class DockerCommandLocator { private static final Pattern PATH_SPLITTER = Pattern.compile(File.pathSeparator); - private final String command; + protected abstract String command(); - public DockerCommandLocator(String command) { - this.command = command; + @Value.Default + protected boolean isWindows() { + return SystemUtils.IS_OS_WINDOWS; } - public String getLocation() { - // Get path variable, ignoring the case of its name - String path = getEnv().entrySet().stream() - .filter(e -> e.getKey().equalsIgnoreCase("path")) - .findFirst() - .map(Map.Entry::getValue) - .orElseThrow(() -> new IllegalStateException("Could not find path variable in env")); + @Value.Derived + protected String executableName() { + if (isWindows()) { + return command() + ".exe"; + } + return command(); + } + + @Value.Default + protected String path() { + String path = System.getenv("path"); + if (path == null) { + throw new IllegalStateException("No path environment variable found"); + } + return path; + } - // The filename is the same as the command, except on Windows where it ends with ".exe" - String filename = isWindows() ? command + ".exe" : command; + protected abstract Optional locationOverride(); - // Look through the directories in path for the given command which must exist and be executable - return PATH_SPLITTER.splitAsStream(path) - .map(p -> Paths.get(p, filename)) + public String getLocation() { + Stream overrideLocation = locationOverride().map(l -> Stream.of(l)).orElse(Stream.empty()); + Stream pathLocations = PATH_SPLITTER.splitAsStream(path()); + return Stream.concat(overrideLocation, pathLocations) + .map(p -> Paths.get(p, executableName())) .filter(Files::exists) .findFirst() .map(Path::toString) - .orElseThrow(() -> new IllegalStateException("Could not find " + command + " in path")); - } - - protected Map getEnv() { - return System.getenv(); - } - - protected boolean isWindows() { - return SystemUtils.IS_OS_WINDOWS; + .orElseThrow(() -> new IllegalStateException("Could not find " + command() + " in path")); } - @Override - public String toString() { - return "DockerCommandLocator{command=" + command + "}"; - } } From 35ab870f6a6125e677bac23af5e0362257238358 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Tue, 7 Mar 2017 14:50:54 +0000 Subject: [PATCH 03/20] Docker for Mac is not in the path when tests run from IDE. --- .../execution/DockerCommandLocator.java | 37 ++++++-- .../execution/DockerComposeExecutable.java | 5 +- .../compose/execution/DockerExecutable.java | 5 +- .../execution/DockerCommandLocatorShould.java | 88 +++++++++++-------- 4 files changed, 89 insertions(+), 46 deletions(-) diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java index 8ba2da174..f1b0758a7 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java @@ -19,9 +19,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Stream; +import javax.annotation.Nullable; import org.apache.commons.lang3.SystemUtils; import org.immutables.value.Value; @@ -46,19 +46,39 @@ protected String executableName() { @Value.Default protected String path() { - String path = System.getenv("path"); + String path = System.getenv("PATH"); if (path == null) { throw new IllegalStateException("No path environment variable found"); } return path; } - protected abstract Optional locationOverride(); + @Value.Check + protected void pathIsNotEmpty() { + if (path().isEmpty()) { + throw new IllegalStateException("Path variable was empty"); + } + } + + @Nullable + protected abstract String locationOverride(); + + @Value.Default + protected Stream macSearchLocations() { + return Stream.of("/usr/local/bin", "/usr/bin"); + } + + @Value.Derived + protected Stream searchLocations() { + Stream pathLocations = Stream.concat(PATH_SPLITTER.splitAsStream(path()), macSearchLocations()); + if (locationOverride() == null) { + return pathLocations; + } + return Stream.concat(Stream.of(locationOverride()), pathLocations); + } public String getLocation() { - Stream overrideLocation = locationOverride().map(l -> Stream.of(l)).orElse(Stream.empty()); - Stream pathLocations = PATH_SPLITTER.splitAsStream(path()); - return Stream.concat(overrideLocation, pathLocations) + return searchLocations() .map(p -> Paths.get(p, executableName())) .filter(Files::exists) .findFirst() @@ -66,4 +86,9 @@ public String getLocation() { .orElseThrow(() -> new IllegalStateException("Could not find " + command() + " in path")); } + public static ImmutableDockerCommandLocator.Builder forCommand(String command) { + return ImmutableDockerCommandLocator.builder() + .command(command); + } + } diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecutable.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecutable.java index 15927abd0..48a54697e 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecutable.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecutable.java @@ -30,7 +30,10 @@ public abstract class DockerComposeExecutable implements Executable { private static final Logger log = LoggerFactory.getLogger(DockerComposeExecutable.class); private static String defaultDockerComposePath() { - String pathToUse = new DockerCommandLocator("docker-compose").getLocation(); + DockerCommandLocator commandLocator = DockerCommandLocator.forCommand("docker-compose") + .locationOverride(System.getenv("DOCKER_COMPOSE_LOCATION")) + .build(); + String pathToUse = commandLocator.getLocation(); log.debug("Using docker-compose found at " + pathToUse); return pathToUse; } diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java index ede18a051..8d2378eb0 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java @@ -35,7 +35,10 @@ public final String commandName() { @Value.Derived protected String dockerPath() { - String pathToUse = new DockerCommandLocator("docker").getLocation(); + DockerCommandLocator commandLocator = DockerCommandLocator.forCommand("docker") + .locationOverride(System.getenv("DOCKER_LOCATION")) + .build(); + String pathToUse = commandLocator.getLocation(); log.debug("Using docker found at " + pathToUse); return pathToUse; } diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java index d0322d21f..a7645880a 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java @@ -17,18 +17,13 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -43,64 +38,78 @@ public class DockerCommandLocatorShould { @Rule public ExpectedException exception = ExpectedException.none(); - private final DockerCommandLocator locator = spy(new DockerCommandLocator(command)); - - private final Map env = new HashMap<>(); - private Path emptyFolder; - private Path firstFolder; - - private Path secondFolder; + private Path firstPathFolder; + private Path secondPathFolder; - private String commandFile; - - private String windowsCommandFile; + private String commandFileLocation; + private String windowsCommandFileLocation; private String pathString; @Before public void setup() throws IOException { emptyFolder = folder.newFolder("empty").toPath(); - firstFolder = folder.newFolder("first").toPath(); - secondFolder = folder.newFolder("second").toPath(); + firstPathFolder = folder.newFolder("first").toPath(); + secondPathFolder = folder.newFolder("second").toPath(); - commandFile = Files.createFile(firstFolder.resolve(command)).toString(); - windowsCommandFile = Files.createFile(firstFolder.resolve(windowsCommand)).toString(); - Files.createFile(secondFolder.resolve(command)); - Files.createFile(secondFolder.resolve(windowsCommand)); + commandFileLocation = Files.createFile(firstPathFolder.resolve(command)).toString(); + windowsCommandFileLocation = Files.createFile(firstPathFolder.resolve(windowsCommand)).toString(); + Files.createFile(secondPathFolder.resolve(command)); + Files.createFile(secondPathFolder.resolve(windowsCommand)); - pathString = Stream.of(emptyFolder, firstFolder, secondFolder) + pathString = Stream.of(emptyFolder, firstPathFolder, secondPathFolder) .map(Path::toString) .collect(Collectors.joining(File.pathSeparator)); - - doReturn(env).when(locator).getEnv(); } @Test public void provide_the_first_command_location() { - env.put("path", pathString); - doReturn(false).when(locator).isWindows(); - assertThat(locator.getLocation(), is(commandFile)); + DockerCommandLocator locator = DockerCommandLocator.forCommand(command) + .path(pathString) + .isWindows(false) + .build(); + assertThat(locator.getLocation(), is(commandFileLocation)); + } + + @Test public void + provide_the_first_command_location_on_windows() { + DockerCommandLocator locator = DockerCommandLocator.forCommand(command) + .path(pathString) + .isWindows(true) + .build(); + assertThat(locator.getLocation(), is(windowsCommandFileLocation)); } @Test public void - provide_the_first_command_location_using_capitalised_path() { - env.put("PATH", pathString); - doReturn(false).when(locator).isWindows(); - assertThat(locator.getLocation(), is(commandFile)); + provide_command_in_override_location() { + DockerCommandLocator locator = DockerCommandLocator.forCommand(command) + .path(firstPathFolder.toString()) + .locationOverride(secondPathFolder.toString()) + .isWindows(false) + .build(); + assertThat(locator.getLocation(), is(secondPathFolder.resolve(command).toString())); } @Test public void - provide_the_first_command_location_on_windows() { - env.put("path", pathString); - doReturn(true).when(locator).isWindows(); - assertThat(locator.getLocation(), is(windowsCommandFile)); + provide_command_in_docker_for_mac_install_location() { + DockerCommandLocator locator = DockerCommandLocator.forCommand(command) + .path(emptyFolder.toString()) + .macSearchLocations(Stream.of(secondPathFolder.toString())) + .isWindows(false) + .build(); + assertThat(locator.getLocation(), is(secondPathFolder.resolve(command).toString())); } @Test public void fail_when_no_paths_contain_command() { - env.put("path", emptyFolder.toString()); + DockerCommandLocator locator = DockerCommandLocator.forCommand(command) + .path(emptyFolder.toString()) + .macSearchLocations(Stream.empty()) + .isWindows(false) + .build(); + exception.expect(IllegalStateException.class); exception.expectMessage("Could not find " + command + " in path"); locator.getLocation(); @@ -109,7 +118,10 @@ public void setup() throws IOException { @Test public void fail_when_no_path_variable_is_set() { exception.expect(IllegalStateException.class); - exception.expectMessage("Could not find path variable in env"); - locator.getLocation(); + exception.expectMessage("Path variable was empty"); + + DockerCommandLocator.forCommand(command) + .path("") + .build(); } } From 252a88efb4545c51010add15ee3da888f8fe8ebf Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Tue, 7 Mar 2017 15:55:11 +0000 Subject: [PATCH 04/20] Split out finding possible command locations from testing those locations. --- .../execution/DockerCommandLocations.java | 75 ++++++++++ .../execution/DockerCommandLocator.java | 37 +---- .../DockerCommandLocationsShould.java | 129 ++++++++++++++++++ .../execution/DockerCommandLocatorShould.java | 54 +------- 4 files changed, 212 insertions(+), 83 deletions(-) create mode 100644 docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java create mode 100644 docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java new file mode 100644 index 000000000..1d6608fe5 --- /dev/null +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java @@ -0,0 +1,75 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.execution; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +@Value.Immutable +public abstract class DockerCommandLocations { + + private static final Pattern PATH_SPLITTER = Pattern.compile(File.pathSeparator); + + @Value.Default + protected Map env() { + return System.getenv(); + } + + @Value.Derived + protected String path() { + String path = env().get("PATH"); + if (path == null) { + path = env().get("path"); + } + if (path == null) { + throw new IllegalStateException("No path environment variable found"); + } + return path; + } + + @Value.Check + protected void pathIsNotEmpty() { + if (path().isEmpty()) { + throw new IllegalStateException("Path variable was empty"); + } + } + + @Nullable + protected abstract String locationOverride(); + + @Value.Default + protected Stream macSearchLocations() { + return Stream.of("/usr/local/bin", "/usr/bin"); + } + + private Stream pathLocations() { + Stream pathLocations = Stream.concat(PATH_SPLITTER.splitAsStream(path()), macSearchLocations()); + if (locationOverride() == null) { + return pathLocations; + } + return Stream.concat(Stream.of(locationOverride()), pathLocations); + } + + public Stream forCommand() { + return pathLocations().map(p -> Paths.get(p)); + } + + public static DockerCommandLocations withLocationOverride(String override) { + return ImmutableDockerCommandLocations.builder() + .locationOverride(override) + .build(); + } + + public static ImmutableDockerCommandLocations.Builder builder() { + return ImmutableDockerCommandLocations.builder(); + } + +} diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java index f1b0758a7..2312b93ab 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java @@ -15,19 +15,14 @@ */ package com.palantir.docker.compose.execution; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.regex.Pattern; -import java.util.stream.Stream; import javax.annotation.Nullable; import org.apache.commons.lang3.SystemUtils; import org.immutables.value.Value; @Value.Immutable public abstract class DockerCommandLocator { - private static final Pattern PATH_SPLITTER = Pattern.compile(File.pathSeparator); protected abstract String command(); @@ -44,42 +39,18 @@ protected String executableName() { return command(); } - @Value.Default - protected String path() { - String path = System.getenv("PATH"); - if (path == null) { - throw new IllegalStateException("No path environment variable found"); - } - return path; - } - - @Value.Check - protected void pathIsNotEmpty() { - if (path().isEmpty()) { - throw new IllegalStateException("Path variable was empty"); - } - } - @Nullable protected abstract String locationOverride(); - @Value.Default - protected Stream macSearchLocations() { - return Stream.of("/usr/local/bin", "/usr/bin"); - } - @Value.Derived - protected Stream searchLocations() { - Stream pathLocations = Stream.concat(PATH_SPLITTER.splitAsStream(path()), macSearchLocations()); - if (locationOverride() == null) { - return pathLocations; - } - return Stream.concat(Stream.of(locationOverride()), pathLocations); + protected DockerCommandLocations searchLocations() { + return DockerCommandLocations.withLocationOverride(locationOverride()); } public String getLocation() { return searchLocations() - .map(p -> Paths.get(p, executableName())) + .forCommand() + .map(p -> p.resolve(executableName())) .filter(Files::exists) .findFirst() .map(Path::toString) diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java new file mode 100644 index 000000000..e86c9cc15 --- /dev/null +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java @@ -0,0 +1,129 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.execution; + +import static java.util.stream.Collectors.toList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.junit.Assume.assumeTrue; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +public class DockerCommandLocationsShould { + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Rule public ExpectedException exception = ExpectedException.none(); + + private Path firstPathFolder; + private Path secondPathFolder; + + @Before + public void setup() throws IOException { + firstPathFolder = folder.newFolder("first").toPath(); + secondPathFolder = folder.newFolder("second").toPath(); + } + + @Test public void + contain_the_contents_of_the_path_variable() { + assumeTrue("Path variable present", System.getenv("PATH") != null); + + DockerCommandLocations commandLocations = DockerCommandLocations.builder() + .macSearchLocations(Stream.empty()) + .build(); + Stream foundLocations = commandLocations.forCommand(); + + String[] pathComponents = System.getenv("PATH").split(File.pathSeparator); + List expectedPaths = Arrays.stream(pathComponents) + .map(p -> Paths.get(p)) + .collect(toList()); + assertThat(foundLocations.collect(toList()), is(expectedPaths)); + } + + @Test public void + contain_the_contents_of_the_path_with_a_single_folder() { + DockerCommandLocations commandLocations = DockerCommandLocations.builder() + .env(Collections.singletonMap("PATH", firstPathFolder.toString())) + .macSearchLocations(Stream.empty()) + .build(); + + assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder)); + } + + @Test public void + contain_the_contents_of_the_path_with_two_folders() { + DockerCommandLocations commandLocations = DockerCommandLocations.builder() + .env(Collections.singletonMap("PATH", firstPathFolder.toString() + File.pathSeparator + secondPathFolder.toString())) + .macSearchLocations(Stream.empty()) + .build(); + + assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder, secondPathFolder)); + } + + @Test public void + contain_the_location_override_before_the_contents_of_the_path() { + DockerCommandLocations commandLocations = DockerCommandLocations.builder() + .locationOverride(firstPathFolder.toString()) + .env(Collections.singletonMap("PATH", secondPathFolder.toString())) + .macSearchLocations(Stream.empty()) + .build(); + + assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder, secondPathFolder)); + } + + @Test public void + contain_the_docker_for_mac_install_location_after_the_path() { + DockerCommandLocations commandLocations = DockerCommandLocations.builder() + .env(Collections.singletonMap("PATH", firstPathFolder.toString())) + .macSearchLocations(Stream.of(secondPathFolder.toString())) + .build(); + + assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder, secondPathFolder)); + } + + @Test public void + contain_the_contents_of_the_path_with_a_lowercase_environment_variable() { + DockerCommandLocations commandLocations = DockerCommandLocations.builder() + .env(Collections.singletonMap("path", firstPathFolder.toString())) + .macSearchLocations(Stream.empty()) + .build(); + + assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder)); + } + + @Test public void + throw_an_exception_if_no_path_variable_is_present() { + exception.expect(IllegalStateException.class); + exception.expectMessage("No path environment variable found"); + + DockerCommandLocations.builder() + .env(Collections.emptyMap()) + .build(); + } + + @Test public void + throw_an_exception_if_the_path_variable_is_empty() { + exception.expect(IllegalStateException.class); + exception.expectMessage("Path variable was empty"); + + DockerCommandLocations.builder() + .env(Collections.singletonMap("PATH", "")) + .build(); + } + +} diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java index a7645880a..fc7de8e9b 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java @@ -18,12 +18,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -31,43 +28,30 @@ import org.junit.rules.TemporaryFolder; public class DockerCommandLocatorShould { - private static final String command = "command"; + private static final String command = "not-a-real-command!"; private static final String windowsCommand = command + ".exe"; @Rule public TemporaryFolder folder = new TemporaryFolder(); @Rule public ExpectedException exception = ExpectedException.none(); - private Path emptyFolder; - private Path firstPathFolder; - private Path secondPathFolder; private String commandFileLocation; private String windowsCommandFileLocation; - private String pathString; - @Before public void setup() throws IOException { - emptyFolder = folder.newFolder("empty").toPath(); firstPathFolder = folder.newFolder("first").toPath(); - secondPathFolder = folder.newFolder("second").toPath(); commandFileLocation = Files.createFile(firstPathFolder.resolve(command)).toString(); windowsCommandFileLocation = Files.createFile(firstPathFolder.resolve(windowsCommand)).toString(); - Files.createFile(secondPathFolder.resolve(command)); - Files.createFile(secondPathFolder.resolve(windowsCommand)); - - pathString = Stream.of(emptyFolder, firstPathFolder, secondPathFolder) - .map(Path::toString) - .collect(Collectors.joining(File.pathSeparator)); } @Test public void provide_the_first_command_location() { DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .path(pathString) + .locationOverride(firstPathFolder.toString()) .isWindows(false) .build(); assertThat(locator.getLocation(), is(commandFileLocation)); @@ -76,37 +60,16 @@ public void setup() throws IOException { @Test public void provide_the_first_command_location_on_windows() { DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .path(pathString) + .locationOverride(firstPathFolder.toString()) .isWindows(true) .build(); assertThat(locator.getLocation(), is(windowsCommandFileLocation)); } - @Test public void - provide_command_in_override_location() { - DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .path(firstPathFolder.toString()) - .locationOverride(secondPathFolder.toString()) - .isWindows(false) - .build(); - assertThat(locator.getLocation(), is(secondPathFolder.resolve(command).toString())); - } - - @Test public void - provide_command_in_docker_for_mac_install_location() { - DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .path(emptyFolder.toString()) - .macSearchLocations(Stream.of(secondPathFolder.toString())) - .isWindows(false) - .build(); - assertThat(locator.getLocation(), is(secondPathFolder.resolve(command).toString())); - } - @Test public void fail_when_no_paths_contain_command() { DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .path(emptyFolder.toString()) - .macSearchLocations(Stream.empty()) + .locationOverride(null) .isWindows(false) .build(); @@ -115,13 +78,4 @@ public void setup() throws IOException { locator.getLocation(); } - @Test public void - fail_when_no_path_variable_is_set() { - exception.expect(IllegalStateException.class); - exception.expectMessage("Path variable was empty"); - - DockerCommandLocator.forCommand(command) - .path("") - .build(); - } } From 0a61921568c1404b3ff7f2458bbf46467a373f5f Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Tue, 7 Mar 2017 16:03:06 +0000 Subject: [PATCH 05/20] Add jsr dep --- docker-compose-rule-core/build.gradle | 2 +- .../docker/compose/execution/DockerCommandLocations.java | 2 +- .../palantir/docker/compose/execution/DockerCommandLocator.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose-rule-core/build.gradle b/docker-compose-rule-core/build.gradle index 7159dad63..660c561be 100644 --- a/docker-compose-rule-core/build.gradle +++ b/docker-compose-rule-core/build.gradle @@ -5,6 +5,7 @@ dependencies { compile "com.google.guava:guava:$guavaVersion" compile "joda-time:joda-time:$jodaVersion" compile "com.github.zafarkhaja:java-semver:$javaSemverVersion" + compile "com.google.code.findbugs:jsr305:3.0.0" compile 'com.jayway.awaitility:awaitility:1.6.5' @@ -18,7 +19,6 @@ dependencies { testCompile "org.hamcrest:hamcrest-all:$hamcrestVersion" 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" } diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java index 1d6608fe5..67427d875 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java @@ -62,7 +62,7 @@ public Stream forCommand() { return pathLocations().map(p -> Paths.get(p)); } - public static DockerCommandLocations withLocationOverride(String override) { + public static DockerCommandLocations withOverride(String override) { return ImmutableDockerCommandLocations.builder() .locationOverride(override) .build(); diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java index 2312b93ab..eaf206a56 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java @@ -44,7 +44,7 @@ protected String executableName() { @Value.Derived protected DockerCommandLocations searchLocations() { - return DockerCommandLocations.withLocationOverride(locationOverride()); + return DockerCommandLocations.withOverride(locationOverride()); } public String getLocation() { From d39fb318b377f45b74a3ea73545846eb0038fa7b Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Wed, 8 Mar 2017 10:51:03 +0000 Subject: [PATCH 06/20] Refactor to simply logic and improve test coverage by using EnvironmentVariables junit rule. --- .baseline/findbugs/excludeFilter.xml | 1 + .../execution/DockerCommandLocations.java | 83 +++++---------- .../execution/DockerCommandLocator.java | 15 ++- .../DockerCommandLocationsShould.java | 100 +++--------------- .../execution/DockerCommandLocatorShould.java | 25 ++++- 5 files changed, 76 insertions(+), 148 deletions(-) diff --git a/.baseline/findbugs/excludeFilter.xml b/.baseline/findbugs/excludeFilter.xml index 47b8bfda0..d96c8a012 100644 --- a/.baseline/findbugs/excludeFilter.xml +++ b/.baseline/findbugs/excludeFilter.xml @@ -16,6 +16,7 @@ + diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java index 67427d875..330925297 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java @@ -1,75 +1,46 @@ /* * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.palantir.docker.compose.execution; +import static com.google.common.base.MoreObjects.firstNonNull; +import static java.util.stream.Collectors.toList; + +import com.google.common.collect.ImmutableList; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Map; -import java.util.regex.Pattern; +import java.util.Arrays; +import java.util.List; import java.util.stream.Stream; -import javax.annotation.Nullable; -import org.immutables.value.Value; - -@Value.Immutable -public abstract class DockerCommandLocations { - private static final Pattern PATH_SPLITTER = Pattern.compile(File.pathSeparator); - - @Value.Default - protected Map env() { - return System.getenv(); - } +public class DockerCommandLocations { - @Value.Derived - protected String path() { - String path = env().get("PATH"); - if (path == null) { - path = env().get("path"); - } - if (path == null) { - throw new IllegalStateException("No path environment variable found"); - } - return path; - } - - @Value.Check - protected void pathIsNotEmpty() { - if (path().isEmpty()) { - throw new IllegalStateException("Path variable was empty"); - } - } + private static final List MAC_SEARCH_LOCATIONS = ImmutableList.of("/usr/local/bin", "/usr/bin"); - @Nullable - protected abstract String locationOverride(); - - @Value.Default - protected Stream macSearchLocations() { - return Stream.of("/usr/local/bin", "/usr/bin"); - } - - private Stream pathLocations() { - Stream pathLocations = Stream.concat(PATH_SPLITTER.splitAsStream(path()), macSearchLocations()); - if (locationOverride() == null) { - return pathLocations; - } - return Stream.concat(Stream.of(locationOverride()), pathLocations); - } - - public Stream forCommand() { - return pathLocations().map(p -> Paths.get(p)); - } + private DockerCommandLocations() { - public static DockerCommandLocations withOverride(String override) { - return ImmutableDockerCommandLocations.builder() - .locationOverride(override) - .build(); } - public static ImmutableDockerCommandLocations.Builder builder() { - return ImmutableDockerCommandLocations.builder(); + public static List pathLocations() { + String path = firstNonNull(System.getenv("PATH"), firstNonNull(System.getenv("path"), File.pathSeparator)); + Stream pathLocations = Arrays.stream(path.split(File.pathSeparator)); + return Stream.concat(pathLocations, MAC_SEARCH_LOCATIONS.stream()) + .map(p -> Paths.get(p)) + .collect(toList()); } } diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java index eaf206a56..ee096b785 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java @@ -15,8 +15,12 @@ */ package com.palantir.docker.compose.execution; +import static java.util.Collections.singletonList; + import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; import javax.annotation.Nullable; import org.apache.commons.lang3.SystemUtils; import org.immutables.value.Value; @@ -43,18 +47,21 @@ protected String executableName() { protected abstract String locationOverride(); @Value.Derived - protected DockerCommandLocations searchLocations() { - return DockerCommandLocations.withOverride(locationOverride()); + protected List searchLocations() { + if (locationOverride() == null) { + return DockerCommandLocations.pathLocations(); + } + return singletonList(Paths.get(locationOverride())); } public String getLocation() { return searchLocations() - .forCommand() + .stream() .map(p -> p.resolve(executableName())) .filter(Files::exists) .findFirst() .map(Path::toString) - .orElseThrow(() -> new IllegalStateException("Could not find " + command() + " in path")); + .orElseThrow(() -> new IllegalStateException("Could not find " + command() + " in " + searchLocations())); } public static ImmutableDockerCommandLocator.Builder forCommand(String command) { diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java index e86c9cc15..48b99ed92 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java @@ -4,126 +4,58 @@ package com.palantir.docker.compose.execution; -import static java.util.stream.Collectors.toList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.is; -import static org.junit.Assume.assumeTrue; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; import java.io.File; -import java.io.IOException; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Stream; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; public class DockerCommandLocationsShould { - @Rule public TemporaryFolder folder = new TemporaryFolder(); + @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Rule public ExpectedException exception = ExpectedException.none(); - private Path firstPathFolder; - private Path secondPathFolder; - - @Before - public void setup() throws IOException { - firstPathFolder = folder.newFolder("first").toPath(); - secondPathFolder = folder.newFolder("second").toPath(); - } - - @Test public void - contain_the_contents_of_the_path_variable() { - assumeTrue("Path variable present", System.getenv("PATH") != null); - - DockerCommandLocations commandLocations = DockerCommandLocations.builder() - .macSearchLocations(Stream.empty()) - .build(); - Stream foundLocations = commandLocations.forCommand(); - - String[] pathComponents = System.getenv("PATH").split(File.pathSeparator); - List expectedPaths = Arrays.stream(pathComponents) - .map(p -> Paths.get(p)) - .collect(toList()); - assertThat(foundLocations.collect(toList()), is(expectedPaths)); - } - @Test public void contain_the_contents_of_the_path_with_a_single_folder() { - DockerCommandLocations commandLocations = DockerCommandLocations.builder() - .env(Collections.singletonMap("PATH", firstPathFolder.toString())) - .macSearchLocations(Stream.empty()) - .build(); + environmentVariables.set("PATH", "/folder"); - assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder)); + assertThat(DockerCommandLocations.pathLocations(), hasItem(Paths.get("/folder"))); } @Test public void contain_the_contents_of_the_path_with_two_folders() { - DockerCommandLocations commandLocations = DockerCommandLocations.builder() - .env(Collections.singletonMap("PATH", firstPathFolder.toString() + File.pathSeparator + secondPathFolder.toString())) - .macSearchLocations(Stream.empty()) - .build(); + environmentVariables.set("PATH", "/first" + File.pathSeparator + "/second"); - assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder, secondPathFolder)); - } - - @Test public void - contain_the_location_override_before_the_contents_of_the_path() { - DockerCommandLocations commandLocations = DockerCommandLocations.builder() - .locationOverride(firstPathFolder.toString()) - .env(Collections.singletonMap("PATH", secondPathFolder.toString())) - .macSearchLocations(Stream.empty()) - .build(); - - assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder, secondPathFolder)); + assertThat(DockerCommandLocations.pathLocations(), hasItems(Paths.get("/first"), Paths.get("/second"))); } @Test public void contain_the_docker_for_mac_install_location_after_the_path() { - DockerCommandLocations commandLocations = DockerCommandLocations.builder() - .env(Collections.singletonMap("PATH", firstPathFolder.toString())) - .macSearchLocations(Stream.of(secondPathFolder.toString())) - .build(); + environmentVariables.set("PATH", "/folder"); - assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder, secondPathFolder)); + assertThat(DockerCommandLocations.pathLocations(), contains(Paths.get("/folder"), Paths.get("/usr/local/bin"), Paths.get("/usr/bin"))); } @Test public void contain_the_contents_of_the_path_with_a_lowercase_environment_variable() { - DockerCommandLocations commandLocations = DockerCommandLocations.builder() - .env(Collections.singletonMap("path", firstPathFolder.toString())) - .macSearchLocations(Stream.empty()) - .build(); - - assertThat(commandLocations.forCommand().collect(toList()), contains(firstPathFolder)); - } - - @Test public void - throw_an_exception_if_no_path_variable_is_present() { - exception.expect(IllegalStateException.class); - exception.expectMessage("No path environment variable found"); + environmentVariables.set("PATH", null); + environmentVariables.set("path", "/lowercase"); - DockerCommandLocations.builder() - .env(Collections.emptyMap()) - .build(); + assertThat(DockerCommandLocations.pathLocations(), hasItem(Paths.get("/lowercase"))); } @Test public void - throw_an_exception_if_the_path_variable_is_empty() { - exception.expect(IllegalStateException.class); - exception.expectMessage("Path variable was empty"); + return_just_the_mac_locations_if_no_path_variable_is_present() { + environmentVariables.set("PATH", null); - DockerCommandLocations.builder() - .env(Collections.singletonMap("PATH", "")) - .build(); + assertThat(DockerCommandLocations.pathLocations(), contains(Paths.get("/usr/local/bin"), Paths.get("/usr/bin"))); } } diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java index fc7de8e9b..ac1e8acba 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; @@ -35,6 +36,8 @@ public class DockerCommandLocatorShould { @Rule public ExpectedException exception = ExpectedException.none(); + @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); + private Path firstPathFolder; private String commandFileLocation; @@ -46,12 +49,13 @@ public void setup() throws IOException { commandFileLocation = Files.createFile(firstPathFolder.resolve(command)).toString(); windowsCommandFileLocation = Files.createFile(firstPathFolder.resolve(windowsCommand)).toString(); + + environmentVariables.set("PATH", firstPathFolder.toString()); } @Test public void provide_the_first_command_location() { DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .locationOverride(firstPathFolder.toString()) .isWindows(false) .build(); assertThat(locator.getLocation(), is(commandFileLocation)); @@ -60,7 +64,6 @@ public void setup() throws IOException { @Test public void provide_the_first_command_location_on_windows() { DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .locationOverride(firstPathFolder.toString()) .isWindows(true) .build(); assertThat(locator.getLocation(), is(windowsCommandFileLocation)); @@ -68,14 +71,28 @@ public void setup() throws IOException { @Test public void fail_when_no_paths_contain_command() { + environmentVariables.set("PATH", null); + DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .locationOverride(null) .isWindows(false) .build(); exception.expect(IllegalStateException.class); - exception.expectMessage("Could not find " + command + " in path"); + exception.expectMessage("Could not find " + command + " in [/usr/local/bin, /usr/bin]"); locator.getLocation(); } + @Test public void + allow_the_path_to_be_overriden() throws IOException { + Path overrideFolder = folder.newFolder("override").toPath(); + String overridenCommand = Files.createFile(overrideFolder.resolve(command)).toString(); + + DockerCommandLocator locator = DockerCommandLocator.forCommand(command) + .locationOverride(overrideFolder.toString()) + .isWindows(false) + .build(); + + assertThat(locator.getLocation(), is(overridenCommand)); + } + } From 19791dd6a73d94d7deb892f38a80ace0661ab68f Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Wed, 8 Mar 2017 10:55:45 +0000 Subject: [PATCH 07/20] Add case for Windows path variable "Path" --- .../docker/compose/execution/DockerCommandLocations.java | 6 ++++-- .../compose/execution/DockerCommandLocationsShould.java | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java index 330925297..189e580df 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java @@ -16,7 +16,6 @@ package com.palantir.docker.compose.execution; -import static com.google.common.base.MoreObjects.firstNonNull; import static java.util.stream.Collectors.toList; import com.google.common.collect.ImmutableList; @@ -36,7 +35,10 @@ private DockerCommandLocations() { } public static List pathLocations() { - String path = firstNonNull(System.getenv("PATH"), firstNonNull(System.getenv("path"), File.pathSeparator)); + String path = Stream.of(System.getenv("PATH"), System.getenv("path"), System.getenv("Path")) + .filter(s -> s != null) + .findFirst() + .orElse(File.pathSeparator); Stream pathLocations = Arrays.stream(path.split(File.pathSeparator)); return Stream.concat(pathLocations, MAC_SEARCH_LOCATIONS.stream()) .map(p -> Paths.get(p)) diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java index 48b99ed92..19026cd84 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java @@ -51,6 +51,14 @@ public class DockerCommandLocationsShould { assertThat(DockerCommandLocations.pathLocations(), hasItem(Paths.get("/lowercase"))); } + @Test public void + contain_the_contents_of_the_path_with_a_windows_environment_variable() { + environmentVariables.set("PATH", null); + environmentVariables.set("Path", "/windows"); + + assertThat(DockerCommandLocations.pathLocations(), hasItem(Paths.get("/windows"))); + } + @Test public void return_just_the_mac_locations_if_no_path_variable_is_present() { environmentVariables.set("PATH", null); From a734f16a6036699312d9b743e3f9fb55e9ffcf24 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Wed, 8 Mar 2017 16:20:36 +0000 Subject: [PATCH 08/20] Process builder will look for command on the path --- .../execution/DockerCommandLocations.java | 48 ------------- .../execution/DockerCommandLocator.java | 27 ++++---- .../compose/execution/DockerExecutable.java | 1 + .../DockerCommandLocationsShould.java | 69 ------------------- .../execution/DockerCommandLocatorShould.java | 50 +++++--------- 5 files changed, 32 insertions(+), 163 deletions(-) delete mode 100644 docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java delete mode 100644 docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java deleted file mode 100644 index 189e580df..000000000 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocations.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.docker.compose.execution; - -import static java.util.stream.Collectors.toList; - -import com.google.common.collect.ImmutableList; -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Stream; - -public class DockerCommandLocations { - - private static final List MAC_SEARCH_LOCATIONS = ImmutableList.of("/usr/local/bin", "/usr/bin"); - - private DockerCommandLocations() { - - } - - public static List pathLocations() { - String path = Stream.of(System.getenv("PATH"), System.getenv("path"), System.getenv("Path")) - .filter(s -> s != null) - .findFirst() - .orElse(File.pathSeparator); - Stream pathLocations = Arrays.stream(path.split(File.pathSeparator)); - return Stream.concat(pathLocations, MAC_SEARCH_LOCATIONS.stream()) - .map(p -> Paths.get(p)) - .collect(toList()); - } - -} diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java index ee096b785..402d9c8a2 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java @@ -15,8 +15,7 @@ */ package com.palantir.docker.compose.execution; -import static java.util.Collections.singletonList; - +import com.google.common.collect.ImmutableList; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -28,6 +27,8 @@ @Value.Immutable public abstract class DockerCommandLocator { + private static final List MAC_SEARCH_LOCATIONS = ImmutableList.of("/usr/local/bin", "/usr/bin"); + protected abstract String command(); @Value.Default @@ -35,6 +36,11 @@ protected boolean isWindows() { return SystemUtils.IS_OS_WINDOWS; } + @Value.Default + protected List macSearchLocations() { + return MAC_SEARCH_LOCATIONS; + } + @Value.Derived protected String executableName() { if (isWindows()) { @@ -46,22 +52,17 @@ protected String executableName() { @Nullable protected abstract String locationOverride(); - @Value.Derived - protected List searchLocations() { - if (locationOverride() == null) { - return DockerCommandLocations.pathLocations(); - } - return singletonList(Paths.get(locationOverride())); - } - public String getLocation() { - return searchLocations() + if (locationOverride() != null) { + return locationOverride(); + } + return macSearchLocations() .stream() - .map(p -> p.resolve(executableName())) + .map(p -> Paths.get(p, executableName())) .filter(Files::exists) .findFirst() .map(Path::toString) - .orElseThrow(() -> new IllegalStateException("Could not find " + command() + " in " + searchLocations())); + .orElse(executableName()); } public static ImmutableDockerCommandLocator.Builder forCommand(String command) { diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java index 8d2378eb0..5dc9cf755 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerExecutable.java @@ -59,4 +59,5 @@ public Process execute(String... commands) throws IOException { public static ImmutableDockerExecutable.Builder builder() { return ImmutableDockerExecutable.builder(); } + } diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java deleted file mode 100644 index 19026cd84..000000000 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocationsShould.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - */ - -package com.palantir.docker.compose.execution; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasItems; - -import java.io.File; -import java.nio.file.Paths; -import org.junit.Rule; -import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; -import org.junit.rules.ExpectedException; - -public class DockerCommandLocationsShould { - - @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); - - @Rule public ExpectedException exception = ExpectedException.none(); - - @Test public void - contain_the_contents_of_the_path_with_a_single_folder() { - environmentVariables.set("PATH", "/folder"); - - assertThat(DockerCommandLocations.pathLocations(), hasItem(Paths.get("/folder"))); - } - - @Test public void - contain_the_contents_of_the_path_with_two_folders() { - environmentVariables.set("PATH", "/first" + File.pathSeparator + "/second"); - - assertThat(DockerCommandLocations.pathLocations(), hasItems(Paths.get("/first"), Paths.get("/second"))); - } - - @Test public void - contain_the_docker_for_mac_install_location_after_the_path() { - environmentVariables.set("PATH", "/folder"); - - assertThat(DockerCommandLocations.pathLocations(), contains(Paths.get("/folder"), Paths.get("/usr/local/bin"), Paths.get("/usr/bin"))); - } - - @Test public void - contain_the_contents_of_the_path_with_a_lowercase_environment_variable() { - environmentVariables.set("PATH", null); - environmentVariables.set("path", "/lowercase"); - - assertThat(DockerCommandLocations.pathLocations(), hasItem(Paths.get("/lowercase"))); - } - - @Test public void - contain_the_contents_of_the_path_with_a_windows_environment_variable() { - environmentVariables.set("PATH", null); - environmentVariables.set("Path", "/windows"); - - assertThat(DockerCommandLocations.pathLocations(), hasItem(Paths.get("/windows"))); - } - - @Test public void - return_just_the_mac_locations_if_no_path_variable_is_present() { - environmentVariables.set("PATH", null); - - assertThat(DockerCommandLocations.pathLocations(), contains(Paths.get("/usr/local/bin"), Paths.get("/usr/bin"))); - } - -} diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java index ac1e8acba..d32348db3 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java @@ -15,16 +15,15 @@ */ package com.palantir.docker.compose.execution; +import static java.util.Collections.singletonList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; @@ -36,63 +35,48 @@ public class DockerCommandLocatorShould { @Rule public ExpectedException exception = ExpectedException.none(); - @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); - - private Path firstPathFolder; - - private String commandFileLocation; - private String windowsCommandFileLocation; - - @Before - public void setup() throws IOException { - firstPathFolder = folder.newFolder("first").toPath(); - - commandFileLocation = Files.createFile(firstPathFolder.resolve(command)).toString(); - windowsCommandFileLocation = Files.createFile(firstPathFolder.resolve(windowsCommand)).toString(); - - environmentVariables.set("PATH", firstPathFolder.toString()); - } - @Test public void - provide_the_first_command_location() { + returns_the_command_name_when_no_other_paths_contain_command() { DockerCommandLocator locator = DockerCommandLocator.forCommand(command) .isWindows(false) .build(); - assertThat(locator.getLocation(), is(commandFileLocation)); + + assertThat(locator.getLocation(), is(command)); } @Test public void - provide_the_first_command_location_on_windows() { + returns_the_command_name_with_exe_on_windows_when_no_other_paths_contain_command() { DockerCommandLocator locator = DockerCommandLocator.forCommand(command) .isWindows(true) .build(); - assertThat(locator.getLocation(), is(windowsCommandFileLocation)); + + assertThat(locator.getLocation(), is(windowsCommand)); } @Test public void - fail_when_no_paths_contain_command() { - environmentVariables.set("PATH", null); + allow_the_path_to_be_overriden() throws IOException { + Path overrideFolder = folder.newFolder("override").toPath(); + String overridenCommand = Files.createFile(overrideFolder.resolve(command)).toString(); DockerCommandLocator locator = DockerCommandLocator.forCommand(command) + .locationOverride(overridenCommand) .isWindows(false) .build(); - exception.expect(IllegalStateException.class); - exception.expectMessage("Could not find " + command + " in [/usr/local/bin, /usr/bin]"); - locator.getLocation(); + assertThat(locator.getLocation(), is(overridenCommand)); } @Test public void - allow_the_path_to_be_overriden() throws IOException { - Path overrideFolder = folder.newFolder("override").toPath(); - String overridenCommand = Files.createFile(overrideFolder.resolve(command)).toString(); + search_in_known_mac_install_locations_for_the_command() throws IOException { + Path macSearchFolder = folder.newFolder("override").toPath(); + String commandLocation = Files.createFile(macSearchFolder.resolve(command)).toString(); DockerCommandLocator locator = DockerCommandLocator.forCommand(command) - .locationOverride(overrideFolder.toString()) + .macSearchLocations(singletonList(macSearchFolder.toString())) .isWindows(false) .build(); - assertThat(locator.getLocation(), is(overridenCommand)); + assertThat(locator.getLocation(), is(commandLocation)); } } From 215f42740de2c4d57b425d791e8277f701428ebe Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Wed, 8 Mar 2017 17:45:00 +0000 Subject: [PATCH 09/20] Move integ tests into separate source set. --- build.gradle | 1 + .../connection/ContainerIntegrationTests.java | 4 +- .../resources/native-healthcheck.yaml | 0 .../resources/no-healthcheck.yaml | 0 .../docker/compose/execution/Docker.java | 3 +- .../compose/execution/DockerShould.java | 2 +- ...essiveShutdownStrategyIntegrationTest.java | 2 +- ...NetworkCleanupStrategyIntegrationTest.java | 2 +- .../DockerComposeRuleIntegrationTest.java | 2 +- ...eRuleNativeHealthcheckIntegrationTest.java | 2 +- .../compose/DockerComposeRuleShould.java | 0 ...ComposeRuleUpContainerIntegrationTest.java | 2 +- .../HostNetworkedPortsIntegrationTest.java | 3 +- ...eConflictingContainersIntegrationTest.java | 2 +- .../EnvironmentVariableIntegrationTest.java | 2 +- .../logging/LoggingIntegrationTest.java | 2 +- .../resources/docker-compose.yaml | 0 .../resources/environment/docker-compose.yaml | 0 .../host-networked-docker-compose.yaml | 0 .../named-containers-docker-compose.yaml | 0 .../resources/native-healthcheck.yaml | 0 .../shutdown-strategy-with-network.yaml | 0 .../resources/shutdown-strategy.yaml | 0 .../resources/up-integration-test.yaml | 0 gradle/integTest.gradle | 67 +++++++++++++++++++ 25 files changed, 81 insertions(+), 15 deletions(-) rename docker-compose-rule-core/src/{test => integTest}/java/com/palantir/docker/compose/connection/ContainerIntegrationTests.java (95%) rename docker-compose-rule-core/src/{test => integTest}/resources/native-healthcheck.yaml (100%) rename docker-compose-rule-core/src/{test => integTest}/resources/no-healthcheck.yaml (100%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/AggressiveShutdownStrategyIntegrationTest.java (93%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/AggressiveShutdownWithNetworkCleanupStrategyIntegrationTest.java (95%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/DockerComposeRuleIntegrationTest.java (98%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/DockerComposeRuleNativeHealthcheckIntegrationTest.java (97%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/DockerComposeRuleShould.java (100%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/DockerComposeRuleUpContainerIntegrationTest.java (97%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java (96%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/RemoveConflictingContainersIntegrationTest.java (97%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/execution/EnvironmentVariableIntegrationTest.java (96%) rename docker-compose-rule-junit4/src/{test => integTest}/java/com/palantir/docker/compose/logging/LoggingIntegrationTest.java (97%) rename docker-compose-rule-junit4/src/{test => integTest}/resources/docker-compose.yaml (100%) rename docker-compose-rule-junit4/src/{test => integTest}/resources/environment/docker-compose.yaml (100%) rename docker-compose-rule-junit4/src/{test => integTest}/resources/host-networked-docker-compose.yaml (100%) rename docker-compose-rule-junit4/src/{test => integTest}/resources/named-containers-docker-compose.yaml (100%) rename docker-compose-rule-junit4/src/{test => integTest}/resources/native-healthcheck.yaml (100%) rename docker-compose-rule-junit4/src/{test => integTest}/resources/shutdown-strategy-with-network.yaml (100%) rename docker-compose-rule-junit4/src/{test => integTest}/resources/shutdown-strategy.yaml (100%) rename docker-compose-rule-junit4/src/{test => integTest}/resources/up-integration-test.yaml (100%) create mode 100644 gradle/integTest.gradle diff --git a/build.gradle b/build.gradle index 43a25c96b..934aa4a75 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,7 @@ subprojects { apply plugin: 'com.palantir.baseline-eclipse' apply plugin: 'com.palantir.baseline-findbugs' apply plugin: 'com.palantir.baseline-idea' + apply from: "${rootDir}/gradle/integTest.gradle" apply from: "${rootDir}/gradle/publish.gradle" apply plugin: 'org.inferred.processors' diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/connection/ContainerIntegrationTests.java b/docker-compose-rule-core/src/integTest/java/com/palantir/docker/compose/connection/ContainerIntegrationTests.java similarity index 95% rename from docker-compose-rule-core/src/test/java/com/palantir/docker/compose/connection/ContainerIntegrationTests.java rename to docker-compose-rule-core/src/integTest/java/com/palantir/docker/compose/connection/ContainerIntegrationTests.java index e2ecfa8aa..d4892c551 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/connection/ContainerIntegrationTests.java +++ b/docker-compose-rule-core/src/integTest/java/com/palantir/docker/compose/connection/ContainerIntegrationTests.java @@ -36,7 +36,7 @@ public class ContainerIntegrationTests { @Test public void testStateChanges_withoutHealthCheck() throws IOException, InterruptedException { DockerCompose dockerCompose = new DefaultDockerCompose( - DockerComposeFiles.from("src/test/resources/no-healthcheck.yaml"), + DockerComposeFiles.from("src/integTest/resources/no-healthcheck.yaml"), dockerMachine, ProjectName.random()); @@ -60,7 +60,7 @@ public void testStateChanges_withHealthCheck() throws IOException, InterruptedEx assumeThat("docker-compose version", DockerCompose.version(), new GreaterOrEqual<>(Version.forIntegers(1, 10, 0))); DockerCompose dockerCompose = new DefaultDockerCompose( - DockerComposeFiles.from("src/test/resources/native-healthcheck.yaml"), + DockerComposeFiles.from("src/integTest/resources/native-healthcheck.yaml"), dockerMachine, ProjectName.random()); diff --git a/docker-compose-rule-core/src/test/resources/native-healthcheck.yaml b/docker-compose-rule-core/src/integTest/resources/native-healthcheck.yaml similarity index 100% rename from docker-compose-rule-core/src/test/resources/native-healthcheck.yaml rename to docker-compose-rule-core/src/integTest/resources/native-healthcheck.yaml diff --git a/docker-compose-rule-core/src/test/resources/no-healthcheck.yaml b/docker-compose-rule-core/src/integTest/resources/no-healthcheck.yaml similarity index 100% rename from docker-compose-rule-core/src/test/resources/no-healthcheck.yaml rename to docker-compose-rule-core/src/integTest/resources/no-healthcheck.yaml diff --git a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/Docker.java b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/Docker.java index c7ec8aa4c..94c27f407 100644 --- a/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/Docker.java +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/Docker.java @@ -25,7 +25,6 @@ import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +34,7 @@ public class Docker { private static final Logger log = LoggerFactory.getLogger(Docker.class); // Without java escape characters: ^(\d+)\.(\d+)\.(\d+)(?:-.*)?$ - private static final Pattern VERSION_PATTERN = Pattern.compile("^Docker version (\\d+)\\.(\\d+)\\.(\\d+)(?:-.*)?$"); + private static final Pattern VERSION_PATTERN = Pattern.compile("^Docker version (\\d+)\\.(\\d+)\\.(\\d+)(?:(-|,).*)?$"); private static final String HEALTH_STATUS_FORMAT = "--format=" + "{{if not .State.Running}}DOWN" diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerShould.java index a55fbd2aa..73d427533 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerShould.java @@ -62,7 +62,7 @@ public void call_docker_network_ls() throws IOException, InterruptedException { @Test public void understand_old_version_format() throws IOException, InterruptedException { - when(executedProcess.getInputStream()).thenReturn(toInputStream("Docker version 1.7.2")); + when(executedProcess.getInputStream()).thenReturn(toInputStream("Docker version 1.7.2, build abcdef")); Version version = docker.configuredVersion(); assertThat(version, is(Version.valueOf("1.7.2"))); diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/AggressiveShutdownStrategyIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/AggressiveShutdownStrategyIntegrationTest.java similarity index 93% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/AggressiveShutdownStrategyIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/AggressiveShutdownStrategyIntegrationTest.java index 058e15e0d..e5cda5646 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/AggressiveShutdownStrategyIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/AggressiveShutdownStrategyIntegrationTest.java @@ -16,7 +16,7 @@ public class AggressiveShutdownStrategyIntegrationTest { @Test public void shut_down_multiple_containers_immediately() throws Exception { DockerComposeRule rule = DockerComposeRule.builder() - .file("src/test/resources/shutdown-strategy.yaml") + .file("src/integTest/resources/shutdown-strategy.yaml") .logCollector(new DoNothingLogCollector()) .retryAttempts(0) .shutdownStrategy(ShutdownStrategy.AGGRESSIVE) diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/AggressiveShutdownWithNetworkCleanupStrategyIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/AggressiveShutdownWithNetworkCleanupStrategyIntegrationTest.java similarity index 95% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/AggressiveShutdownWithNetworkCleanupStrategyIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/AggressiveShutdownWithNetworkCleanupStrategyIntegrationTest.java index 35eacafa7..41613ce1f 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/AggressiveShutdownWithNetworkCleanupStrategyIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/AggressiveShutdownWithNetworkCleanupStrategyIntegrationTest.java @@ -18,7 +18,7 @@ public class AggressiveShutdownWithNetworkCleanupStrategyIntegrationTest { private final DockerComposeRule rule = DockerComposeRule.builder() - .file("src/test/resources/shutdown-strategy-with-network.yaml") + .file("src/integTest/resources/shutdown-strategy-with-network.yaml") .logCollector(new DoNothingLogCollector()) .retryAttempts(0) .shutdownStrategy(ShutdownStrategy.AGGRESSIVE_WITH_NETWORK_CLEANUP) diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleIntegrationTest.java similarity index 98% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleIntegrationTest.java index 8162b087e..17dca78e5 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleIntegrationTest.java @@ -45,7 +45,7 @@ public class DockerComposeRuleIntegrationTest { @Rule public final DockerComposeRule docker = DockerComposeRule.builder() - .files(DockerComposeFiles.from("src/test/resources/docker-compose.yaml")) + .files(DockerComposeFiles.from("src/integTest/resources/docker-compose.yaml")) .waitingForService("db", toHaveAllPortsOpen()) .waitingForService("db2", toHaveAllPortsOpen()) .waitingForServices(ImmutableList.of("db3", "db4"), toAllHaveAllPortsOpen()) diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleNativeHealthcheckIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleNativeHealthcheckIntegrationTest.java similarity index 97% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleNativeHealthcheckIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleNativeHealthcheckIntegrationTest.java index 6ad62740f..fad8f15fe 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleNativeHealthcheckIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleNativeHealthcheckIntegrationTest.java @@ -53,7 +53,7 @@ public void dockerComposeRuleWaitsUntilHealthcheckPasses() assumeThat("docker-compose version", DockerCompose.version(), new GreaterOrEqual<>(Version.forIntegers(1, 10, 0))); rule = DockerComposeRule.builder() - .file("src/test/resources/native-healthcheck.yaml") + .file("src/integTest/resources/native-healthcheck.yaml") .build(); Future beforeFuture = pool.submit(() -> { rule.before(); diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleShould.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleShould.java similarity index 100% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleShould.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleShould.java diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleUpContainerIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleUpContainerIntegrationTest.java similarity index 97% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleUpContainerIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleUpContainerIntegrationTest.java index 488a2b949..45f96b8ad 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleUpContainerIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleUpContainerIntegrationTest.java @@ -37,7 +37,7 @@ public class DockerComposeRuleUpContainerIntegrationTest { public final DockerComposeRule dockerComposeRule = DockerComposeRule .builder() .shutdownStrategy(AGGRESSIVE) - .file("src/test/resources/up-integration-test.yaml") + .file("src/integTest/resources/up-integration-test.yaml") .build(); @Test diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java similarity index 96% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java index baf59ed37..64f2f117b 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java @@ -22,7 +22,6 @@ import com.palantir.docker.compose.connection.DockerPort; import com.palantir.docker.compose.connection.waiting.HealthCheck; import com.palantir.docker.compose.connection.waiting.SuccessOrFailure; - import org.apache.commons.lang3.SystemUtils; import org.junit.Test; @@ -40,7 +39,7 @@ private static HealthCheck toBeOpen() { assumeTrue("Host ports are only accessible on linux", SystemUtils.IS_OS_LINUX); DockerComposeRule docker = DockerComposeRule.builder() - .file("src/test/resources/host-networked-docker-compose.yaml") + .file("src/integTest/resources/host-networked-docker-compose.yaml") .waitingForHostNetworkedPort(5432, toBeOpen()) .build(); try { diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/RemoveConflictingContainersIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/RemoveConflictingContainersIntegrationTest.java similarity index 97% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/RemoveConflictingContainersIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/RemoveConflictingContainersIntegrationTest.java index 84961cc78..800e8f149 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/RemoveConflictingContainersIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/RemoveConflictingContainersIntegrationTest.java @@ -25,7 +25,7 @@ public class RemoveConflictingContainersIntegrationTest { - private static final String DOCKER_COMPOSE_YAML_PATH = "src/test/resources/named-containers-docker-compose.yaml"; + private static final String DOCKER_COMPOSE_YAML_PATH = "src/integTest/resources/named-containers-docker-compose.yaml"; @Rule public ExpectedException exception = ExpectedException.none(); diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/execution/EnvironmentVariableIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/execution/EnvironmentVariableIntegrationTest.java similarity index 96% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/execution/EnvironmentVariableIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/execution/EnvironmentVariableIntegrationTest.java index 304ba7dcd..25ddfa1ff 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/execution/EnvironmentVariableIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/execution/EnvironmentVariableIntegrationTest.java @@ -39,7 +39,7 @@ public void docker_compose_gets_environment_variables_from_docker_machine_and_pa .build(); DockerComposeRule dockerComposition = DockerComposeRule.builder() - .file("src/test/resources/environment/docker-compose.yaml") + .file("src/integTest/resources/environment/docker-compose.yaml") .machine(dockerMachine) .waitingForService("env-test", toHaveAllPortsOpen()) .saveLogsTo(temporaryFolder.getRoot().getAbsolutePath()) diff --git a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/logging/LoggingIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/logging/LoggingIntegrationTest.java similarity index 97% rename from docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/logging/LoggingIntegrationTest.java rename to docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/logging/LoggingIntegrationTest.java index f227e0bbe..94a569a5d 100644 --- a/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/logging/LoggingIntegrationTest.java +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/logging/LoggingIntegrationTest.java @@ -39,7 +39,7 @@ public class LoggingIntegrationTest { @Before public void setUp() { dockerComposeRule = DockerComposeRule.builder() - .file("src/test/resources/docker-compose.yaml") + .file("src/integTest/resources/docker-compose.yaml") .waitingForService("db", toHaveAllPortsOpen()) .waitingForService("db2", toHaveAllPortsOpen()) .saveLogsTo(logFolder.getRoot().getAbsolutePath()) diff --git a/docker-compose-rule-junit4/src/test/resources/docker-compose.yaml b/docker-compose-rule-junit4/src/integTest/resources/docker-compose.yaml similarity index 100% rename from docker-compose-rule-junit4/src/test/resources/docker-compose.yaml rename to docker-compose-rule-junit4/src/integTest/resources/docker-compose.yaml diff --git a/docker-compose-rule-junit4/src/test/resources/environment/docker-compose.yaml b/docker-compose-rule-junit4/src/integTest/resources/environment/docker-compose.yaml similarity index 100% rename from docker-compose-rule-junit4/src/test/resources/environment/docker-compose.yaml rename to docker-compose-rule-junit4/src/integTest/resources/environment/docker-compose.yaml diff --git a/docker-compose-rule-junit4/src/test/resources/host-networked-docker-compose.yaml b/docker-compose-rule-junit4/src/integTest/resources/host-networked-docker-compose.yaml similarity index 100% rename from docker-compose-rule-junit4/src/test/resources/host-networked-docker-compose.yaml rename to docker-compose-rule-junit4/src/integTest/resources/host-networked-docker-compose.yaml diff --git a/docker-compose-rule-junit4/src/test/resources/named-containers-docker-compose.yaml b/docker-compose-rule-junit4/src/integTest/resources/named-containers-docker-compose.yaml similarity index 100% rename from docker-compose-rule-junit4/src/test/resources/named-containers-docker-compose.yaml rename to docker-compose-rule-junit4/src/integTest/resources/named-containers-docker-compose.yaml diff --git a/docker-compose-rule-junit4/src/test/resources/native-healthcheck.yaml b/docker-compose-rule-junit4/src/integTest/resources/native-healthcheck.yaml similarity index 100% rename from docker-compose-rule-junit4/src/test/resources/native-healthcheck.yaml rename to docker-compose-rule-junit4/src/integTest/resources/native-healthcheck.yaml diff --git a/docker-compose-rule-junit4/src/test/resources/shutdown-strategy-with-network.yaml b/docker-compose-rule-junit4/src/integTest/resources/shutdown-strategy-with-network.yaml similarity index 100% rename from docker-compose-rule-junit4/src/test/resources/shutdown-strategy-with-network.yaml rename to docker-compose-rule-junit4/src/integTest/resources/shutdown-strategy-with-network.yaml diff --git a/docker-compose-rule-junit4/src/test/resources/shutdown-strategy.yaml b/docker-compose-rule-junit4/src/integTest/resources/shutdown-strategy.yaml similarity index 100% rename from docker-compose-rule-junit4/src/test/resources/shutdown-strategy.yaml rename to docker-compose-rule-junit4/src/integTest/resources/shutdown-strategy.yaml diff --git a/docker-compose-rule-junit4/src/test/resources/up-integration-test.yaml b/docker-compose-rule-junit4/src/integTest/resources/up-integration-test.yaml similarity index 100% rename from docker-compose-rule-junit4/src/test/resources/up-integration-test.yaml rename to docker-compose-rule-junit4/src/integTest/resources/up-integration-test.yaml diff --git a/gradle/integTest.gradle b/gradle/integTest.gradle new file mode 100644 index 000000000..bf1dcea1c --- /dev/null +++ b/gradle/integTest.gradle @@ -0,0 +1,67 @@ +// From https://raw.githubusercontent.com/spring-io/sagan/master/gradle/integTest.gradle +import org.gradle.api.tasks.testing.Test + +/** + * This script adds a dedicated integration testing source set (src/integTest) and task + * (integTest) when applied to any Java project. + * + * Configuration: + * apply from: path/to/integTest.gradle + * + * Usage: + * gradle integTest + * - or - + * gradle check + */ + +sourceSets { + integTest { + java.srcDir 'src/integTest/java' + resources.srcDir 'src/integTest/resources' + } +} + +task integTest(type: Test) { + description = "Runs integration tests." + + testClassesDir = sourceSets.integTest.output.classesDir + classpath = sourceSets.integTest.runtimeClasspath + + maxHeapSize = '1024m' + + // ensure we don't overwrite default report directories used by 'test' task + reports.html.destination = "${project.buildDir}/reports/integTest" + reports.junitXml.destination = "${project.buildDir}/integTest-results" + binResultsDir = file("${project.buildDir}/integTest-results/binary/integTest") + + // always run integration tests after unit tests in order to fail fast + mustRunAfter test +} + +dependencies { + integTestCompile sourceSets.main.output + integTestCompile sourceSets.test.output + integTestCompile configurations.compile + integTestCompile configurations.testCompile + integTestRuntime configurations.runtime + integTestRuntime configurations.testRuntime +} + +// Ensure unit test tasks (named 'test') always run before integration tests in +// order to fail as fast as possible +gradle.projectsEvaluated { + def unitTestTasks = [] + gradle.rootProject.allprojects { project -> + unitTestTasks.addAll(project.tasks.findAll { it.name == 'test' }) + } + unitTestTasks.each { unitTestTask -> + project.integTest.mustRunAfter unitTestTask + } +} + +// Add integration test source directories to IDEA .iml files +if (project.plugins.hasPlugin('idea')) { + idea.module { + testSourceDirs += (sourceSets.integTest.java.srcDirs + sourceSets.integTest.resources.srcDirs) + } +} From a8ce98be63323089cbf145bf44240439fe2ce6f7 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Wed, 8 Mar 2017 17:56:19 +0000 Subject: [PATCH 10/20] Add very simple windows smoke test. --- .../windows/WindowsIntegrationTest.java | 36 +++++++++++++++++++ .../resources/windows-docker-compose.yaml | 4 +++ .../compose/DockerComposeRuleShould.java | 0 3 files changed, 40 insertions(+) create mode 100644 docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/windows/WindowsIntegrationTest.java create mode 100644 docker-compose-rule-junit4/src/integTest/resources/windows-docker-compose.yaml rename docker-compose-rule-junit4/src/{integTest => test}/java/com/palantir/docker/compose/DockerComposeRuleShould.java (100%) diff --git a/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/windows/WindowsIntegrationTest.java b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/windows/WindowsIntegrationTest.java new file mode 100644 index 000000000..394004ee9 --- /dev/null +++ b/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/windows/WindowsIntegrationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.windows; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.junit.Assume.assumeTrue; + +import com.palantir.docker.compose.DockerComposeRule; +import com.palantir.docker.compose.configuration.DockerComposeFiles; +import com.palantir.docker.compose.connection.State; +import java.io.IOException; +import org.apache.commons.lang3.SystemUtils; +import org.junit.Test; + +public class WindowsIntegrationTest { + + @Test + public void can_execute_docker_commands() throws IOException, InterruptedException { + assumeTrue("Running on Windows", SystemUtils.IS_OS_WINDOWS); + + DockerComposeRule docker = DockerComposeRule.builder() + .files(DockerComposeFiles.from("src/integTest/resources/windows-docker-compose.yaml")) + .build(); + docker.before(); + + try { + assertThat(docker.containers().container("hello-world").state(), is(State.DOWN)); + } finally { + docker.after(); + } + } + +} diff --git a/docker-compose-rule-junit4/src/integTest/resources/windows-docker-compose.yaml b/docker-compose-rule-junit4/src/integTest/resources/windows-docker-compose.yaml new file mode 100644 index 000000000..d1e98b7bc --- /dev/null +++ b/docker-compose-rule-junit4/src/integTest/resources/windows-docker-compose.yaml @@ -0,0 +1,4 @@ +hello-world: + image: microsoft/windowsservercore + command: echo "Server started" + diff --git a/docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleShould.java b/docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleShould.java similarity index 100% rename from docker-compose-rule-junit4/src/integTest/java/com/palantir/docker/compose/DockerComposeRuleShould.java rename to docker-compose-rule-junit4/src/test/java/com/palantir/docker/compose/DockerComposeRuleShould.java From cc06e21b06fb85cf75682e4f9264289c69290b1b Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Thu, 9 Mar 2017 11:50:33 +0000 Subject: [PATCH 11/20] Wire up smoke test for Windows to appveyor. --- appveyor.yml | 2 +- circle.yml | 2 +- docker-compose-rule-junit4/build.gradle | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d31b56062..9e172f278 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ build_script: - gradlew.bat compileJava compileTestJava --info --no-daemon test_script: - - gradlew.bat dependencies --info --no-daemon + - gradlew.bat test windowsSmokeTest --info --no-daemon branches: only: diff --git a/circle.yml b/circle.yml index 995af171f..6433cc320 100644 --- a/circle.yml +++ b/circle.yml @@ -18,7 +18,7 @@ test: override: - ? > case $CIRCLE_NODE_INDEX in - 0) ./gradlew test --info ;; + 0) ./gradlew test integTest --info ;; 1) ./gradlew findbugsMain findbugsTest checkstyleMain checkstyleTest javadoc --info ;; esac : diff --git a/docker-compose-rule-junit4/build.gradle b/docker-compose-rule-junit4/build.gradle index f9f2fe3a2..f4eec86fc 100644 --- a/docker-compose-rule-junit4/build.gradle +++ b/docker-compose-rule-junit4/build.gradle @@ -23,3 +23,15 @@ dependencies { if (System.env.CIRCLE_TEST_REPORTS) { test.reports.junitXml.destination = file(System.env.CIRCLE_TEST_REPORTS) } + +task windowsSmokeTest(type: Test) { + testClassesDir = sourceSets.integTest.output.classesDir + classpath = sourceSets.integTest.runtimeClasspath + + reports.html.destination = "${project.buildDir}/reports/integTest" + reports.junitXml.destination = "${project.buildDir}/integTest-results" + binResultsDir = file("${project.buildDir}/integTest-results/binary/integTest") + + include '**/windows/**' +} + From 186aba08b5edae172a233c814ef9dd205aee18cc Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Thu, 9 Mar 2017 12:50:29 +0000 Subject: [PATCH 12/20] Thread.sleep isn't accurate on Windows --- .../docker/compose/execution/RetryerShould.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/RetryerShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/RetryerShould.java index 1a2764677..eb1542ae3 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/RetryerShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/RetryerShould.java @@ -61,11 +61,20 @@ public void should_not_pause_after_last_failure() throws Exception { @Test public void retryer_should_wait_after_failure_before_trying_again() throws Exception { + // Force Windows to use more granular sleeps - see http://stackoverflow.com/questions/824110/accurate-sleep-for-java-on-windows + new Thread(() -> { + try { + Thread.sleep(Long.MAX_VALUE); + } catch (InterruptedException e) { + // Ignore + } + }).start(); + Retryer timeRetryer = new Retryer(1, Duration.millis(100)); Stopwatch stopwatch = Stopwatch.createStarted(); when(operation.call()).thenThrow(new DockerExecutionException()).thenAnswer(i -> { - assertThat(stopwatch.elapsed(TimeUnit.MILLISECONDS), greaterThan(100L)); + assertThat(stopwatch.elapsed(TimeUnit.MILLISECONDS), greaterThan(98L)); return "success"; }); From ed0d9cfc4f9a874a56edabe45e56a1abc574e123 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Thu, 9 Mar 2017 13:10:24 +0000 Subject: [PATCH 13/20] Simply don't run the tes that relies upon Thread.sleep on Windows --- .../docker/compose/execution/RetryerShould.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/RetryerShould.java b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/RetryerShould.java index eb1542ae3..0f60b83d6 100644 --- a/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/RetryerShould.java +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/RetryerShould.java @@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.core.Is.is; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -27,6 +28,7 @@ import com.google.common.base.Stopwatch; import com.palantir.docker.compose.utils.MockitoMultiAnswer; import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.SystemUtils; import org.joda.time.Duration; import org.junit.Test; import org.junit.runner.RunWith; @@ -61,14 +63,7 @@ public void should_not_pause_after_last_failure() throws Exception { @Test public void retryer_should_wait_after_failure_before_trying_again() throws Exception { - // Force Windows to use more granular sleeps - see http://stackoverflow.com/questions/824110/accurate-sleep-for-java-on-windows - new Thread(() -> { - try { - Thread.sleep(Long.MAX_VALUE); - } catch (InterruptedException e) { - // Ignore - } - }).start(); + assumeTrue("Not running on Windows as Thread.sleep isn't accurate there", !SystemUtils.IS_OS_WINDOWS); Retryer timeRetryer = new Retryer(1, Duration.millis(100)); From bc6bdd9356d4c32d0d32476dbc355681dd5fc134 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Fri, 10 Mar 2017 14:40:43 +0000 Subject: [PATCH 14/20] Do we have the latest image? From 1df8a3873ecd6fd879c0e7e75b3caccbb2071bc9 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Fri, 10 Mar 2017 14:47:54 +0000 Subject: [PATCH 15/20] Use Windows Server 2016 image --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 9e172f278..7b0c8fe24 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,6 +17,8 @@ branches: cache: - C:\Users\appveyor\.gradle +image: Windows Server 2016 + environment: matrix: - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 From e903dac8512623f33c05c663732c1bad2c32a6f8 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Fri, 10 Mar 2017 14:54:10 +0000 Subject: [PATCH 16/20] Install docker-compose before running tests that need it --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 7b0c8fe24..07ef8641c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,6 +7,7 @@ build_script: - gradlew.bat compileJava compileTestJava --info --no-daemon test_script: + - choco install docker-compose - gradlew.bat test windowsSmokeTest --info --no-daemon branches: From 8c417115c6b24999cc2150116f358c510a88c132 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Fri, 10 Mar 2017 15:00:48 +0000 Subject: [PATCH 17/20] Use image with more up to date docker --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 07ef8641c..30f09530a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ branches: cache: - C:\Users\appveyor\.gradle -image: Windows Server 2016 +image: image: Visual Studio 2017 environment: matrix: From 479efca9c475f4b267b985cf7e307713dabeb2a4 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Fri, 10 Mar 2017 15:02:07 +0000 Subject: [PATCH 18/20] Correct appveyor.yml syntax --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 30f09530a..3b6b155a8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ branches: cache: - C:\Users\appveyor\.gradle -image: image: Visual Studio 2017 +image: Visual Studio 2017 environment: matrix: From 9492fb4b23a9d9869d11918941b3dc7bca96c493 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Fri, 10 Mar 2017 15:24:19 +0000 Subject: [PATCH 19/20] Try cinst --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 3b6b155a8..bec8175e5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ build_script: - gradlew.bat compileJava compileTestJava --info --no-daemon test_script: - - choco install docker-compose + - cinst -y docker-compose - gradlew.bat test windowsSmokeTest --info --no-daemon branches: From eafa3b072882c18f4d4ef4d8c6b2ae8f85454129 Mon Sep 17 00:00:00 2001 From: Huw Pryce Date: Fri, 10 Mar 2017 15:26:25 +0000 Subject: [PATCH 20/20] Try installing both docker and docker-compose on old image --- appveyor.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index bec8175e5..212ec6633 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ build_script: - gradlew.bat compileJava compileTestJava --info --no-daemon test_script: - - cinst -y docker-compose + - cinst -y docker docker-compose - gradlew.bat test windowsSmokeTest --info --no-daemon branches: @@ -18,8 +18,6 @@ branches: cache: - C:\Users\appveyor\.gradle -image: Visual Studio 2017 - environment: matrix: - JAVA_HOME: C:\Program Files\Java\jdk1.8.0