Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@
import static com.jayway.awaitility.Awaitility.await;
import static com.palantir.docker.compose.execution.DockerComposeExecArgument.arguments;
import static com.palantir.docker.compose.execution.DockerComposeExecOption.noOptions;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeThat;

import com.github.zafarkhaja.semver.Version;
import com.palantir.docker.compose.connection.Container;
import com.palantir.docker.compose.connection.State;
import com.palantir.docker.compose.execution.Docker;
import com.palantir.docker.compose.execution.DockerCompose;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
Expand All @@ -38,7 +37,6 @@
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Test;
import org.mockito.internal.matchers.GreaterOrEqual;

public class DockerComposeManagerNativeHealthcheckIntegrationTest {

Expand All @@ -61,12 +59,13 @@ public void shutdownPool() {
@Test
public void dockerComposeManagerWaitsUntilHealthcheckPasses()
throws ExecutionException, IOException, InterruptedException, TimeoutException {
assumeThat("docker version", Docker.version(), new GreaterOrEqual<>(Version.forIntegers(1, 12, 0)));
assumeThat("docker-compose version", DockerCompose.version(), new GreaterOrEqual<>(Version.forIntegers(1, 10, 0)));

docker = new DockerComposeManager.Builder()
.file("src/test/resources/native-healthcheck.yaml")
.build();

assumeThat(docker.docker().version(), greaterThanOrEqualTo(Version.valueOf("1.12.0")));
assumeThat(docker.dockerCompose().version(), greaterThanOrEqualTo(Version.valueOf("1.10.0")));

Future<?> beforeFuture = pool.submit(() -> {
docker.before();
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static com.palantir.docker.compose.execution.DockerComposeExecArgument.arguments;
import static com.palantir.docker.compose.execution.DockerComposeExecOption.noOptions;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeThat;

Expand All @@ -35,7 +36,6 @@
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.internal.matchers.GreaterOrEqual;

public class ContainerIntegrationTests {

Expand Down Expand Up @@ -69,14 +69,14 @@ public void testStateChanges_withoutHealthCheck() throws IOException, Interrupte
*/
@Test
public void testStateChanges_withHealthCheck() throws IOException, InterruptedException {
assumeThat("docker version", Docker.version(), new GreaterOrEqual<>(Version.forIntegers(1, 12, 0)));
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"),
dockerMachine,
ProjectName.random());

assumeThat(docker.version(), greaterThanOrEqualTo(Version.valueOf("1.12.0")));
assumeThat(dockerCompose.version(), greaterThanOrEqualTo(Version.valueOf("1.10.0")));

// The withHealthcheck service's healthcheck checks every 100ms whether the file "healthy" exists
Container container = new Container("withHealthcheck", docker, dockerCompose);
assertEquals(State.DOWN, container.state());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.palantir.docker.compose.connection.ContainerNames;
import com.palantir.docker.compose.connection.DockerMachine;
import com.palantir.docker.compose.connection.Ports;
import com.palantir.docker.compose.helpers.VersionHelper;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
Expand Down Expand Up @@ -64,6 +65,12 @@ public DefaultDockerCompose(DockerComposeExecutable rawExecutable, DockerMachine
this.dockerMachine = dockerMachine;
}

@Override
public Version version() throws IOException, InterruptedException {
String version = command.execute(Command.throwingOnError(), "version", "--short");
return VersionHelper.toSemVer(version);
}

@Override
public void pull() throws IOException, InterruptedException {
command.execute(Command.throwingOnError(), "pull");
Expand Down Expand Up @@ -139,11 +146,6 @@ private void verifyDockerComposeVersionAtLeast(Version targetVersion, String mes
validState(version().greaterThanOrEqualTo(targetVersion), message);
}

private Version version() throws IOException, InterruptedException {
String versionOutput = command.execute(Command.throwingOnError(), "-v");
return DockerComposeVersion.parseFromDockerComposeVersion(versionOutput);
}

private static String[] constructFullDockerComposeExecArguments(DockerComposeExecOption dockerComposeExecOption,
String containerName, DockerComposeExecArgument dockerComposeExecArgument) {
// The "-T" option here disables pseudo-TTY allocation, which is not useful here since we are not using
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.palantir.docker.compose.execution;

import com.github.zafarkhaja.semver.Version;
import com.palantir.docker.compose.connection.Container;
import com.palantir.docker.compose.connection.ContainerName;
import com.palantir.docker.compose.connection.Ports;
Expand All @@ -30,6 +31,11 @@ protected DelegatingDockerCompose(DockerCompose dockerCompose) {
this.dockerCompose = dockerCompose;
}

@Override
public Version version() throws IOException, InterruptedException {
return dockerCompose.version();
}

@Override
public void pull() throws IOException, InterruptedException {
dockerCompose.pull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,13 @@
*/
package com.palantir.docker.compose.execution;

import static com.google.common.base.Preconditions.checkState;

import com.github.zafarkhaja.semver.Version;
import com.google.common.collect.ObjectArrays;
import com.palantir.docker.compose.connection.DockerMachine;
import com.palantir.docker.compose.connection.State;
import com.palantir.docker.compose.helpers.VersionHelper;
import java.io.IOException;
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;
Expand All @@ -33,8 +30,6 @@ 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 String HEALTH_STATUS_FORMAT =
"--format="
+ "{{if not .State.Running}}DOWN"
Expand All @@ -45,26 +40,17 @@ public class Docker {
+ "{{else}}HEALTHY{{end}}";
private static final String HEALTH_STATUS_FORMAT_WINDOWS = HEALTH_STATUS_FORMAT.replaceAll("\"", "`\"");

public static Version version() throws IOException, InterruptedException {
return new Docker(DockerExecutable.builder().dockerConfiguration(DockerMachine.localMachine().build()).build())
.configuredVersion();
}

public Version configuredVersion() throws IOException, InterruptedException {
String versionString = command.execute(Command.throwingOnError(), "-v");
Matcher matcher = VERSION_PATTERN.matcher(versionString);
checkState(matcher.matches(), "Unexpected output of docker -v: %s", versionString);
return Version.forIntegers(Integer.parseInt(matcher.group(1)),
Integer.parseInt(matcher.group(2)),
Integer.parseInt(matcher.group(3)));
}

private final Command command;

public Docker(DockerExecutable rawExecutable) {
this.command = new Command(rawExecutable, log::trace);
}

public Version version() throws IOException, InterruptedException {
String clientVersionString = command.execute(Command.throwingOnError(), "version", "--format", "{{.Client.Version}}");
return VersionHelper.toSemVer(clientVersionString);
}

public State state(String containerId) throws IOException, InterruptedException {
String formatString = SystemUtils.IS_OS_WINDOWS ? HEALTH_STATUS_FORMAT_WINDOWS : HEALTH_STATUS_FORMAT;
String stateString = command.execute(Command.throwingOnError(), "inspect", formatString, containerId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@
import java.util.Optional;

public interface DockerCompose {
static Version version() throws IOException, InterruptedException {
return DockerComposeExecutable.version();
}
Version version() throws IOException, InterruptedException;
void pull() throws IOException, InterruptedException;
void build() throws IOException, InterruptedException;
void up() throws IOException, InterruptedException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package com.palantir.docker.compose.execution;

import com.github.zafarkhaja.semver.Version;
import com.google.common.collect.ImmutableList;
import com.palantir.docker.compose.configuration.DockerComposeFiles;
import com.palantir.docker.compose.configuration.ProjectName;
Expand Down Expand Up @@ -45,27 +44,6 @@ private static String defaultDockerComposePath() {
return pathToUse;
}

static Version version() throws IOException, InterruptedException {
Command dockerCompose = new Command(new Executable() {
@Override
public String commandName() {
return "docker-compose";
}

@Override
public Process execute(String... commands) throws IOException {
List<String> args = ImmutableList.<String>builder()
.add(defaultDockerComposePath())
.add(commands)
.build();
return new ProcessBuilder(args).redirectErrorStream(true).start();
}
}, log::trace);

String versionOutput = dockerCompose.execute(Command.throwingOnError(), "-v");
return DockerComposeVersion.parseFromDockerComposeVersion(versionOutput);
}

@Value.Parameter protected abstract DockerComposeFiles dockerComposeFiles();
@Value.Parameter protected abstract DockerConfiguration dockerConfiguration();

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* (c) Copyright 2019 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.helpers;

import static com.google.common.base.Preconditions.checkState;

import com.github.zafarkhaja.semver.Version;
import com.google.common.base.Joiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* We depend on semantic versioning (https://semver.org/) and use Java SemVer within
* this library to handle versions (https://github.com/zafarkhaja/jsemver).
* Docker however doesn't use exact semantic versioning so this helper does
* some light processing to get simple, useable versions
*/
public enum VersionHelper {
INSTANCE;

// Semantic versions have a structure of <major>.<minor>.<patch>-<pre-release>+<build metadata>
// This regex groups pre-release and build metadata into the "rest" group
// Regex without escape characters: ^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?<rest>.*)$
private static final Pattern BASIC_SEM_VER_PATTERN =
Pattern.compile("^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(?<rest>.*)$");

public static Version toSemVer(String version) {
Matcher matcher = BASIC_SEM_VER_PATTERN.matcher(version);

checkState(matcher.matches(), "Unexpected version structure: %s", version);

String major = dropLeadingZeroes(matcher.group("major"));
String minor = dropLeadingZeroes(matcher.group("minor"));
String patch = dropLeadingZeroes(matcher.group("patch"));

String versionCore = Joiner.on(".").join(major, minor, patch);

return Version.valueOf(versionCore);
}

private static String dropLeadingZeroes(String numericString) {
return String.valueOf(Integer.parseInt(numericString));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void parse_and_returns_container_names_on_ps() throws IOException, Interr
@Test
public void call_docker_compose_with_no_colour_flag_on_logs() throws IOException {
when(executedProcess.getInputStream()).thenReturn(
toInputStream("docker-compose version 1.7.0, build 1ad8866"),
toInputStream("1.7.0"),
toInputStream("logs"));
ByteArrayOutputStream output = new ByteArrayOutputStream();

Expand All @@ -116,8 +116,8 @@ public void call_docker_compose_with_no_container_on_logs() throws IOException {

final Process mockVersionProcess = mock(Process.class);
when(mockVersionProcess.exitValue()).thenReturn(0);
when(mockVersionProcess.getInputStream()).thenReturn(toInputStream("docker-compose version 1.7.0, build 1ad8866"));
when(executor.execute("-v")).thenReturn(mockVersionProcess);
when(mockVersionProcess.getInputStream()).thenReturn(toInputStream("1.7.0"));
when(executor.execute("version", "--short")).thenReturn(mockVersionProcess);
when(executor.execute("logs", "--no-color", "db")).thenReturn(executedProcess);
when(executedProcess.getInputStream()).thenReturn(toInputStream("logs"));
ByteArrayOutputStream output = new ByteArrayOutputStream();
Expand All @@ -130,7 +130,7 @@ public void call_docker_compose_with_no_container_on_logs() throws IOException {
@Test
public void fail_if_docker_compose_version_is_prior_1_7_on_logs()
throws IOException, InterruptedException {
when(executedProcess.getInputStream()).thenReturn(toInputStream("docker-compose version 1.5.6, build 1ad8866"));
when(executedProcess.getInputStream()).thenReturn(toInputStream("1.5.6"));
exception.expect(IllegalStateException.class);
exception.expectMessage("You need at least docker-compose 1.7 to run docker-compose exec");
compose.exec(options("-d"), "container_1", arguments("ls"));
Expand Down Expand Up @@ -188,15 +188,15 @@ public void throw_illegal_state_exception_when_there_is_no_container_found_for_p
@Test
public void pass_concatenated_arguments_to_executor_on_docker_compose_exec()
throws IOException, InterruptedException {
when(executedProcess.getInputStream()).thenReturn(toInputStream("docker-compose version 1.7.0rc1, build 1ad8866"));
when(executedProcess.getInputStream()).thenReturn(toInputStream("1.7.0-rc1"));
compose.exec(options("-d"), "container_1", arguments("ls"));
verify(executor, times(1)).execute("exec", "-T", "-d", "container_1", "ls");
}

@Test
public void fail_if_docker_compose_version_is_prior_1_7_on_docker_compose_exec()
throws IOException, InterruptedException {
when(executedProcess.getInputStream()).thenReturn(toInputStream("docker-compose version 1.5.6, build 1ad8866"));
when(executedProcess.getInputStream()).thenReturn(toInputStream("1.5.6"));
exception.expect(IllegalStateException.class);
exception.expectMessage("You need at least docker-compose 1.7 to run docker-compose exec");
compose.exec(options("-d"), "container_1", arguments("ls"));
Expand All @@ -214,11 +214,11 @@ public void return_the_output_from_the_executed_process_on_docker_compose_exec()
String lsString = String.format("-rw-r--r-- 1 user 1318458867 11326 Mar 9 17:47 LICENSE%n"
+ "-rw-r--r-- 1 user 1318458867 12570 May 12 14:51 README.md");

String versionString = "docker-compose version 1.7.0rc1, build 1ad8866";
String versionString = "1.7.0-rc1";

DockerComposeExecutable processExecutor = mock(DockerComposeExecutable.class);

addProcessToExecutor(processExecutor, processWithOutput(versionString), "-v");
addProcessToExecutor(processExecutor, processWithOutput(versionString), "version", "--short");
addProcessToExecutor(processExecutor, processWithOutput(lsString), "exec", "-T", "container_1", "ls", "-l");

DockerCompose processCompose = new DefaultDockerCompose(processExecutor, dockerMachine);
Expand Down
Loading