Skip to content

Commit e89dce1

Browse files
candrewsrnorth
andauthored
Allow for docker timestamps with timezone offsets (#4073)
Docker prefers to use UTC time when making images/containers, but it's happy to parse non-UTC times too. It will then pass those on in its output. DateTimeFormatter.ISO_OFFSET_DATE_TIME accepts a superset of what DateTimeFormatter.ISO_INSTANT accepts, so replace use of ISO_INSTANT with ISO_OFFSET_DATE_TIME. Co-authored-by: Richard North <[email protected]>
1 parent 014500b commit e89dce1

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

core/src/main/java/org/testcontainers/utility/DockerStatus.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public static boolean isContainerRunning(InspectContainerResponse.ContainerState
4040
if (minimumRunningDuration == null) {
4141
return true;
4242
}
43-
Instant startedAt = DateTimeFormatter.ISO_INSTANT.parse(
43+
Instant startedAt = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(
4444
state.getStartedAt(), Instant::from);
4545

4646
if (startedAt.isBefore(now.minus(minimumRunningDuration))) {
@@ -75,7 +75,7 @@ public static boolean isDockerTimestampNonEmpty(String dockerTimestamp) {
7575
return dockerTimestamp != null
7676
&& !dockerTimestamp.isEmpty()
7777
&& !dockerTimestamp.equals(DOCKER_TIMESTAMP_ZERO)
78-
&& DateTimeFormatter.ISO_INSTANT.parse(dockerTimestamp, Instant::from).getEpochSecond() >= 0L;
78+
&& DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dockerTimestamp, Instant::from).getEpochSecond() >= 0L;
7979
}
8080

8181
public static boolean isContainerExitCodeSuccess(InspectContainerResponse.ContainerState state) {

core/src/test/java/org/testcontainers/utility/DockerStatusTest.java

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
import com.github.dockerjava.api.command.InspectContainerResponse;
44
import org.junit.Test;
5+
import org.junit.runner.RunWith;
6+
import org.junit.runners.Parameterized;
57
import org.mockito.Mockito;
68

79
import java.time.Duration;
810
import java.time.Instant;
11+
import java.time.ZoneId;
912
import java.time.format.DateTimeFormatter;
1013

1114
import static org.junit.Assert.assertFalse;
@@ -15,33 +18,47 @@
1518
/**
1619
*
1720
*/
21+
@RunWith(Parameterized.class)
1822
public class DockerStatusTest {
23+
private final DateTimeFormatter dateTimeFormatter;
1924

20-
private static Instant now = Instant.now();
25+
private static final Instant now = Instant.now();
2126

22-
private static InspectContainerResponse.ContainerState running =
23-
buildState(true, false, buildTimestamp(now.minusMillis(30)), DockerStatus.DOCKER_TIMESTAMP_ZERO);
27+
private final InspectContainerResponse.ContainerState running;
2428

25-
private static InspectContainerResponse.ContainerState runningVariant =
26-
buildState(true, false, buildTimestamp(now.minusMillis(30)), "");
29+
private final InspectContainerResponse.ContainerState runningVariant;
2730

28-
private static InspectContainerResponse.ContainerState shortRunning =
29-
buildState(true, false, buildTimestamp(now.minusMillis(10)), DockerStatus.DOCKER_TIMESTAMP_ZERO);
31+
private final InspectContainerResponse.ContainerState shortRunning;
3032

31-
private static InspectContainerResponse.ContainerState created =
32-
buildState(false, false, DockerStatus.DOCKER_TIMESTAMP_ZERO, DockerStatus.DOCKER_TIMESTAMP_ZERO);
33+
private final InspectContainerResponse.ContainerState created;
3334

3435
// a container in the "created" state is not running, and has both startedAt and finishedAt empty.
35-
private static InspectContainerResponse.ContainerState createdVariant =
36-
buildState(false, false, null, null);
36+
private final InspectContainerResponse.ContainerState createdVariant;
3737

38-
private static InspectContainerResponse.ContainerState exited =
39-
buildState(false, false, buildTimestamp(now.minusMillis(100)), buildTimestamp(now.minusMillis(90)));
38+
private final InspectContainerResponse.ContainerState exited;
4039

41-
private static InspectContainerResponse.ContainerState paused =
42-
buildState(false, true, buildTimestamp(now.minusMillis(100)), DockerStatus.DOCKER_TIMESTAMP_ZERO);
40+
private final InspectContainerResponse.ContainerState paused;
4341

44-
private static Duration minimumDuration = Duration.ofMillis(20);
42+
private static final Duration minimumDuration = Duration.ofMillis(20);
43+
44+
public DockerStatusTest(DateTimeFormatter dateTimeFormatter) {
45+
this.dateTimeFormatter = dateTimeFormatter;
46+
running = buildState(true, false, buildTimestamp(now.minusMillis(30)), DockerStatus.DOCKER_TIMESTAMP_ZERO);
47+
runningVariant = buildState(true, false, buildTimestamp(now.minusMillis(30)), "");
48+
shortRunning = buildState(true, false, buildTimestamp(now.minusMillis(10)), DockerStatus.DOCKER_TIMESTAMP_ZERO);
49+
created = buildState(false, false, DockerStatus.DOCKER_TIMESTAMP_ZERO, DockerStatus.DOCKER_TIMESTAMP_ZERO);
50+
createdVariant = buildState(false, false, null, null);
51+
exited = buildState(false, false, buildTimestamp(now.minusMillis(100)), buildTimestamp(now.minusMillis(90)));
52+
paused = buildState(false, true, buildTimestamp(now.minusMillis(100)), DockerStatus.DOCKER_TIMESTAMP_ZERO);
53+
}
54+
55+
@Parameterized.Parameters
56+
public static Object[][] parameters() {
57+
return new Object[][] {
58+
{ DateTimeFormatter.ISO_INSTANT},
59+
{ DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("America/New_York")) }
60+
};
61+
}
4562

4663
@Test
4764
public void testRunning() throws Exception {
@@ -65,8 +82,8 @@ public void testStopped() throws Exception {
6582
assertFalse(DockerStatus.isContainerStopped(paused));
6683
}
6784

68-
private static String buildTimestamp(Instant instant) {
69-
return DateTimeFormatter.ISO_INSTANT.format(instant);
85+
private String buildTimestamp(Instant instant) {
86+
return dateTimeFormatter.format(instant);
7087
}
7188

7289
// ContainerState is a non-static inner class, with private member variables, in a different package.

0 commit comments

Comments
 (0)