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(); + } +}