Skip to content

Commit e099725

Browse files
committed
Fix parsing of docker-compose < 1.25.2 output on narrow terminals
1 parent 916c91a commit e099725

File tree

4 files changed

+62
-10
lines changed

4 files changed

+62
-10
lines changed

docker-compose-rule-core/src/integrationTest/java/com/palantir/docker/compose/EventsIntegrationTest.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package com.palantir.docker.compose;
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
20-
import static org.assertj.core.api.Assertions.fail;
20+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2121
import static org.mockito.Mockito.atLeastOnce;
2222
import static org.mockito.Mockito.mock;
2323
import static org.mockito.Mockito.verify;
@@ -87,18 +87,15 @@ public void produces_events_when_a_container_healthcheck_exceeds_its_timeout() {
8787
.addEventConsumer(eventConsumer)
8888
.build();
8989

90-
try {
91-
runDockerComposeRule(dockerComposeManager);
92-
fail("Was expecting an exception");
93-
} catch (Throwable t) {
94-
// ignore the failure
95-
}
90+
assertThatThrownBy(() -> runDockerComposeRule(dockerComposeManager), "Was expecting an exception")
91+
.isInstanceOf(Throwable.class);
9692

9793
List<Event> events = getEvents();
9894

9995
ClusterWaitEvent clusterWait = events.stream()
10096
.map(this::isClusterWait)
10197
.filter(Optional::isPresent)
98+
.filter(e -> e.get().getTask().getFailure().isPresent())
10299
.map(Optional::get)
103100
.findFirst()
104101
.orElseThrow(() -> new IllegalStateException("no clusterwaits in events"));

docker-compose-rule-core/src/main/java/com/palantir/docker/compose/connection/Ports.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
import com.google.common.base.Preconditions;
1919
import com.google.common.base.Strings;
2020
import java.util.ArrayList;
21+
import java.util.Arrays;
2122
import java.util.Collections;
2223
import java.util.List;
2324
import java.util.Objects;
2425
import java.util.regex.Matcher;
2526
import java.util.regex.Pattern;
27+
import java.util.stream.Collectors;
2628
import java.util.stream.Stream;
2729

2830
public final class Ports {
@@ -56,7 +58,30 @@ public Stream<DockerPort> stream() {
5658

5759
public static Ports parseFromDockerComposePs(String psOutput, String dockerMachineIp) {
5860
Preconditions.checkArgument(!Strings.isNullOrEmpty(psOutput), "No container found");
59-
Matcher matcher = PORT_PATTERN.matcher(psOutput);
61+
final String[] lines = psOutput.split("\\R");
62+
String cleanOutput = psOutput;
63+
// Clean actual ps output to remove line breaks and unneeded spaces
64+
if (lines.length > 1) {
65+
int lineWithDashes = -1;
66+
for (int i = 0; i < lines.length; i++) {
67+
if (lines[i].startsWith("-------")) {
68+
lineWithDashes = i;
69+
break;
70+
}
71+
}
72+
Preconditions.checkArgument(lineWithDashes > -1, "No container found");
73+
final int linesToSkip = lineWithDashes + 1;
74+
Preconditions.checkArgument(lines.length > linesToSkip, "No container found");
75+
final int lastIndexOfFieldSeparator = lines[linesToSkip].lastIndexOf(" ");
76+
Preconditions.checkArgument(lastIndexOfFieldSeparator > -1, "Invalid input");
77+
78+
cleanOutput = Arrays.stream(lines)
79+
.skip(linesToSkip)
80+
.map(l -> l.substring(lastIndexOfFieldSeparator + 3))
81+
.map(String::trim)
82+
.collect(Collectors.joining(""));
83+
}
84+
Matcher matcher = PORT_PATTERN.matcher(cleanOutput);
6085
List<DockerPort> ports = new ArrayList<>();
6186
while (matcher.find()) {
6287
String matchedIpAddress = matcher.group(IP_ADDRESS);
@@ -66,7 +91,7 @@ public static Ports parseFromDockerComposePs(String psOutput, String dockerMachi
6691

6792
ports.add(new DockerPort(ip, externalPort, internalPort));
6893
}
69-
Matcher rangeMatcher = PORT_RANGE_PATTERN.matcher(psOutput);
94+
Matcher rangeMatcher = PORT_RANGE_PATTERN.matcher(cleanOutput);
7095
while (rangeMatcher.find()) {
7196
String matchedIpAddress = rangeMatcher.group(IP_ADDRESS);
7297
String ip = matchedIpAddress.equals(NO_IP_ADDRESS) ? dockerMachineIp : matchedIpAddress;

docker-compose-rule-core/src/main/java/com/palantir/docker/compose/execution/Docker.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public final class Docker {
3434
private static final Logger log = LoggerFactory.getLogger(Docker.class);
3535

3636
// Without java escape characters: ^(\d+)\.(\d+)\.(\d+)(?:-.*)?$
37-
private static final Pattern VERSION_PATTERN = Pattern.compile("^Docker version (\\d+)\\.(\\d+)\\.(\\d+)(?:-.*)?$");
37+
private static final Pattern VERSION_PATTERN =
38+
Pattern.compile("^Docker version (\\d+)\\.(\\d+)\\.(\\d+)(?:[,-].*)?$");
3839
private static final String HEALTH_STATUS_FORMAT = "--format="
3940
+ "{{if not .State.Running}}DOWN"
4041
+ "{{else if .State.Paused}}PAUSED"

docker-compose-rule-core/src/test/java/com/palantir/docker/compose/connection/PortsShould.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,35 @@ public void parse_actual_docker_compose_output() {
9595
assertThat(ports, is(expected));
9696
}
9797

98+
@Test
99+
public void parse_docker_compose_output_on_narrow_terminal() {
100+
String psOutput = " Name Command State Ports \n"
101+
+ "--------------------------------------------------------------------------------\n"
102+
+ "projectnamemyserv docker-entrypoint.sh Up (healthy) 4510/tcp, 4511/tcp,\n"
103+
+ "ice_localstack_1 4512/tcp, 4513/tcp,\n"
104+
+ " 4514/tcp, 4515/tcp,\n"
105+
+ " 4558/tcp, 4559/tcp,\n"
106+
+ " 0.0.0.0:49153->4566\n"
107+
+ " /tcp,:::49153->4566\n"
108+
+ " /tcp, 5678/tcp ";
109+
Ports ports = Ports.parseFromDockerComposePs(psOutput, LOCALHOST_IP);
110+
Ports expected = new Ports(newArrayList(new DockerPort(LOCALHOST_IP, 49153, 4566)));
111+
assertThat(ports, is(expected));
112+
}
113+
114+
@Test
115+
public void parse_docker_compose_output_on_narrow_terminal_2() {
116+
String psOutput = " Name Command State Ports \n"
117+
+ "------------------------------------------------------------\n"
118+
+ "decisiontable docker- Up (healthy) 4559/tcp, 0.0\n"
119+
+ "microservice_ entrypoint.sh .0.0:49187->4\n"
120+
+ "localstack_1 566/tcp, \n"
121+
+ " 5678/tcp ";
122+
Ports ports = Ports.parseFromDockerComposePs(psOutput, LOCALHOST_IP);
123+
Ports expected = new Ports(newArrayList(new DockerPort(LOCALHOST_IP, 49187, 4566)));
124+
assertThat(ports, is(expected));
125+
}
126+
98127
@Test
99128
public void throw_illegal_state_exception_when_no_running_container_found_for_service() {
100129
exception.expect(IllegalArgumentException.class);

0 commit comments

Comments
 (0)