Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/look in path #175

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open

Feature/look in path #175

wants to merge 10 commits into from

Conversation

hpryce
Copy link
Contributor

@hpryce hpryce commented Mar 7, 2017

This combines #170 and #152 to provide support for finding docker from the path both on Windows and in existing setups.

Sam Wright and others added 6 commits March 2, 2017 10:03
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.
@@ -1,47 +1,75 @@
/*
* Copyright 2016 Palantir Technologies, Inc. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we keeping or removing the License? It's removed here but kept in the following file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All files should have the Apache licence. I'll fix the ones that had it removed.

return Stream.concat(Stream.of(locationOverride()), pathLocations);
}

public Stream<Path> forCommand() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we returning Stream? They have use once semantics

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Purely because it was extracted out the middle of a stream, I'll make it a list.

protected abstract String command();

@Value.Default
protected boolean isWindows() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returning a constant to represent a default where the constant is framed as a question is somewhat misleading (I still don't know what the default is)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is to allow it to be overriden in tests, otherwise it should behave as a constant.

Would "useWindowsExecutableNaming" have been easier to understand?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isWindows is fine, but I'm starting to think that making it a default doesn't really make any sense. Whoever creates this builder will know at the time what environment they're in, and it feels odd to be like "I'm not going to specify if I'm running on windows because defaults"

Unless of course this is for backcompat and it's used in client code then nevermind.

.orElseThrow(() -> new IllegalStateException(
"Could not find docker-compose, looked in: " + DOCKER_COMPOSE_LOCATIONS));

DockerCommandLocator commandLocator = DockerCommandLocator.forCommand("docker-compose")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returning a builder just to do this looks kinda meh, might as well do the entire builder from here and delete the forCommand method

}

public Optional<String> preferredLocation() {
@Value.Derived
protected String path() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work on Windows, where the path variable is "Path". You could add another if (path == null) path = env().get("Path"), or iterate through the env() map to find any key that equals "path" ignoring case, e.g.

        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"));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, this should now cover PATH, path and Path.

}

@Test public void
fail_when_no_paths_contain_command() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, ignore this one. I got an error running this but I now can't recreate it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, found it again (I had @ignore'd the test, woops!)

Expected: (an instance of java.lang.IllegalStateException and exception with message a string containing "Could not find not-a-real-command! in [/usr/local/bin, /usr/bin]") but: exception with message a string containing "Could not find not-a-real-command! in [/usr/local/bin, /usr/bin]" message was "Could not find not-a-real-command! in [\usr\local\bin, \usr\bin]"

The "/usr/local/bin" string is converted to a Path in DockerCommandLocations.pathLocations(), which on Windows converts it to "\usr\local\bin" (aren't path separators fun?!)

You could change the expected message to just "Could not find " + command, or create a List<Path> defaultPaths = ImmutableList.of(Paths.get("/usr/local/bin"), Paths.get("/usr/bin")) and expect "Could not find " + command + " in " + defaultPaths.

public String toString() {
return "DockerCommandLocations{possiblePaths=" + possiblePaths + "}";
public static List<Path> pathLocations() {
String path = Stream.of(System.getenv("PATH"), System.getenv("path"), System.getenv("Path"))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what about CrazyOS that uses "pATh"? /s

return singletonList(Paths.get(locationOverride()));
}

public String getLocation() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously DOCKER_COMPOSE_LOCATION would be the path to the binary, e.g. /path/to/docker-compose, but the logic here requires it to point to the directory in which to find docker-compose, i.e. /path/to. I think the previous behaviour is more obvious/expected.

You could remove the searchLocations method, and do the check for the location override here instead, e..g

if (locationOverride() == null) {
  return DockerCommandLocations.pathLocations().stream()....;
} else {
  return locationOverride();
}

It might even be worth moving DockerCommandLocations.pathLocations() to this class, but that's just a design thing.

@hpryce
Copy link
Contributor Author

hpryce commented Mar 8, 2017

Okay, it turns out ProcessBuilder looks for commands on the path (at least on Mac/Linux) and so there is no need to do this manually. I've updated the PR to do the simpler thing of only looking in the known Mac install locations and allow for explicitly setting it through an environment variable.

@samwright could you give this a spin and see if it is actually sufficient for Windows? If not we should find a library that will give us as because as @ChrisAlice pointed out the Windows path variable can contain expansions that would require us to look at other environment variables to correctly use.

@samwright
Copy link

Yep this works in Windows. I also tried just running "docker-compose.exe" without specifying its location and that worked too:

ProcessBuilder pb = new ProcessBuilder("docker-compose.exe", "-v");
Process p = pb.start();
p.waitFor();
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8));
System.out.println(r.lines().collect(Collectors.toList())); // Prints: docker-compose version 1.11.2, build f963d76f

So it would probably be best to get rid of all that path variable stuff. Sorry for giving you a red herring!

@dotCipher
Copy link
Contributor

@hpryce , is this still relevant / needed to merge? Or is this overcome-by-events?

@nedtwigg
Copy link

On windows, the just-released 0.34.0 is giving me Could not find docker-compose, even though docker-compose.exe is right on the path. Seems like this PR would fix it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants