diff --git a/.baseline/findbugs/excludeFilter.xml b/.baseline/findbugs/excludeFilter.xml index 1887223f2..d96c8a012 100644 --- a/.baseline/findbugs/excludeFilter.xml +++ b/.baseline/findbugs/excludeFilter.xml @@ -9,13 +9,14 @@ - + + 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 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..402d9c8a2 --- /dev/null +++ b/docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/DockerCommandLocator.java @@ -0,0 +1,73 @@ +/* + * 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 com.google.common.collect.ImmutableList; +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; + +@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 + protected boolean isWindows() { + return SystemUtils.IS_OS_WINDOWS; + } + + @Value.Default + protected List macSearchLocations() { + return MAC_SEARCH_LOCATIONS; + } + + @Value.Derived + protected String executableName() { + if (isWindows()) { + return command() + ".exe"; + } + return command(); + } + + @Nullable + protected abstract String locationOverride(); + + public String getLocation() { + if (locationOverride() != null) { + return locationOverride(); + } + return macSearchLocations() + .stream() + .map(p -> Paths.get(p, executableName())) + .filter(Files::exists) + .findFirst() + .map(Path::toString) + .orElse(executableName()); + } + + 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 9c2daed21..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 @@ -29,19 +29,12 @@ 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)); - + 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 1ff5cf01e..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 @@ -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,11 @@ 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)); - + DockerCommandLocator commandLocator = DockerCommandLocator.forCommand("docker") + .locationOverride(System.getenv("DOCKER_LOCATION")) + .build(); + String pathToUse = commandLocator.getLocation(); log.debug("Using docker found at " + pathToUse); - return pathToUse; } @@ -66,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 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..d32348db3 --- /dev/null +++ b/docker-compose-rule-core/src/test/java/com/palantir/docker/compose/execution/DockerCommandLocatorShould.java @@ -0,0 +1,82 @@ +/* + * 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.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.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +public class DockerCommandLocatorShould { + 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(); + + @Test public void + returns_the_command_name_when_no_other_paths_contain_command() { + DockerCommandLocator locator = DockerCommandLocator.forCommand(command) + .isWindows(false) + .build(); + + assertThat(locator.getLocation(), is(command)); + } + + @Test public void + 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(windowsCommand)); + } + + @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(overridenCommand) + .isWindows(false) + .build(); + + assertThat(locator.getLocation(), is(overridenCommand)); + } + + @Test public void + 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) + .macSearchLocations(singletonList(macSearchFolder.toString())) + .isWindows(false) + .build(); + + assertThat(locator.getLocation(), is(commandLocation)); + } + +}