-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Add DockerHealthcheckWaitStrategy #618
Changes from 1 commit
184ca99
5dd6229
98ef3dd
5dc9bd1
e92538f
f03659a
c8fcbac
4de4253
79cc554
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,8 @@ | |
|
||
public interface ContainerState { | ||
|
||
String STATE_HEALTHY = "healthy"; | ||
|
||
/** | ||
* Get the IP address that this container may be reached on (may not be the local machine). | ||
* | ||
|
@@ -35,6 +37,17 @@ default Boolean isRunning() { | |
} | ||
} | ||
|
||
/** | ||
* @return has the container health state 'healthy'? | ||
*/ | ||
default Boolean isHealthy() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unwrap to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, |
||
try { | ||
return getContainerId() != null && DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec().getState().getHealth().getStatus().equals(STATE_HEALTHY); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you please extract the result of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gladly, I simply copied this from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, but that was an old code and we should improve bit by bit :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to do a little refactoring here and wanted to simply use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, of course. Most of getters (i.e. exposed ports) use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the clarification. I've thus added the |
||
} catch (DockerException e) { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* Get the actual mapped port for a first port exposed by the container. | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package org.testcontainers.containers.wait.strategy; | ||
|
||
import org.rnorth.ducttape.TimeoutException; | ||
import org.rnorth.ducttape.unreliables.Unreliables; | ||
import org.testcontainers.containers.ContainerLaunchException; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* Wait strategy leveraging Docker's built-in healthcheck mechanism. | ||
* | ||
* @see <a href="https://docs.docker.com/engine/reference/builder/#healthcheck">https://docs.docker.com/engine/reference/builder/#healthcheck</a> | ||
*/ | ||
public class DockerHealthcheckWaitStrategy extends AbstractWaitStrategy { | ||
|
||
@Override | ||
protected void waitUntilReady() { | ||
|
||
try { | ||
Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> waitStrategyTarget.isHealthy()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} catch (TimeoutException e) { | ||
throw new ContainerLaunchException("Timed out waiting for container to become healthy"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package org.testcontainers.containers.wait.strategy; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.testcontainers.containers.ContainerLaunchException; | ||
import org.testcontainers.containers.GenericContainer; | ||
import org.testcontainers.images.builder.ImageFromDockerfile; | ||
|
||
import java.time.Duration; | ||
|
||
import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; | ||
|
||
public class DockerHealthcheckWaitStrategyTest { | ||
|
||
private GenericContainer container; | ||
|
||
@Before | ||
public void setUp() { | ||
// Using a Dockerfile here, since Dockerfile builder DSL doesn't support HEALTHCHECK | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitly, but another PR I assume? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure, up to you |
||
container = new GenericContainer(new ImageFromDockerfile() | ||
.withFileFromClasspath("write_file_and_loop.sh", "health-wait-strategy-dockerfile/write_file_and_loop.sh") | ||
.withFileFromClasspath("Dockerfile", "health-wait-strategy-dockerfile/Dockerfile")) | ||
.waitingFor(new DockerHealthcheckWaitStrategy().withStartupTimeout(Duration.ofSeconds(3))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason not to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, I'll change it. |
||
} | ||
|
||
@Test | ||
public void startsOnceHealthy() { | ||
container.start(); | ||
} | ||
|
||
@Test | ||
public void containerStartFailsIfContainerIsUnhealthy() { | ||
container.withCommand("ash"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this test fails because container exits immediately and not because of the healthcheck. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I looked into it, but I can check again. But yes, good idea. |
||
assertThrows("Container launch fails when unhealthy", ContainerLaunchException.class, container::start); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
FROM alpine:latest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please pin the version |
||
|
||
HEALTHCHECK --interval=1s CMD test -e /testfile | ||
|
||
ADD write_file_and_loop.sh write_file_and_loop.sh | ||
RUN chmod +x write_file_and_loop.sh | ||
|
||
CMD ["/write_file_and_loop.sh"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#!/bin/ash | ||
|
||
echo sleeping | ||
sleep 2 | ||
echo writing file | ||
touch /testfile | ||
|
||
while true; do sleep 1; done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR link? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, there was no PR when I added this line 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the same problem every time I submit a PR :D