Skip to content

Commit

Permalink
chore: Configure wait strategy for Testcontainers
Browse files Browse the repository at this point in the history
- Allows users to configure custom wait strategy on the Testcontainers instance
- Supports wait for log message, Http URL, custom wait strategy
- Users may also disable the wait strategy at all
  • Loading branch information
christophd committed Dec 5, 2024
1 parent 0c414c9 commit 7c6c7a7
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.citrusframework.testcontainers.actions;

import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
Expand All @@ -30,6 +31,9 @@
import org.citrusframework.testcontainers.TestContainersSettings;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategyTarget;
import org.testcontainers.utility.MountableFile;

import static org.citrusframework.testcontainers.TestcontainersHelper.getEnvVarName;
Expand Down Expand Up @@ -127,6 +131,8 @@ public static abstract class AbstractBuilder<C extends GenericContainer<?>, T ex

protected final Map<MountableFile, String> volumeMounts = new HashMap<>();

protected WaitStrategy waitStrategy;

private boolean autoRemoveResources = TestContainersSettings.isAutoRemoveResources();

public B containerName(String name) {
Expand Down Expand Up @@ -244,6 +250,43 @@ public B addPortBindings(List<String> bindings) {
return self;
}

public B waitFor(WaitStrategy waitStrategy) {
this.waitStrategy = waitStrategy;
return self;
}

public B waitFor(URL url) {
if ("https".equals(url.getProtocol())) {
this.waitStrategy = Wait.forHttps(url.getPath());
} else {
this.waitStrategy = Wait.forHttp(url.getPath());
}
return self;
}

public B waitFor(String logMessage) {
return waitFor(logMessage, 1);
}

public B waitFor(String logMessage, int times) {
this.waitStrategy = Wait.forLogMessage(logMessage, times);
return self;
}

public B waitStrategyDisabled() {
this.waitStrategy = new WaitStrategy() {
@Override
public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) {
}

@Override
public WaitStrategy withStartupTimeout(Duration startupTimeout) {
return this;
}
};
return self;
}

public B withVolumeMount(MountableFile mountableFile, String containerPath) {
this.volumeMounts.put(mountableFile, containerPath);
return self;
Expand Down Expand Up @@ -279,8 +322,6 @@ protected C buildContainer() {
}
}

container.withStartupTimeout(startupTimeout);

return container;
}

Expand All @@ -293,6 +334,12 @@ protected void configureContainer(C container) {

volumeMounts.forEach(container::withCopyFileToContainer);

if (waitStrategy != null) {
container.waitingFor(waitStrategy);
}

container.withStartupTimeout(startupTimeout);

if (!commandLine.isEmpty()) {
container.withCommand(commandLine.toArray(String[]::new));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.citrusframework.testcontainers.xml;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
Expand All @@ -28,6 +30,7 @@
import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.XmlValue;
import org.citrusframework.TestActor;
import org.citrusframework.exceptions.CitrusRuntimeException;
import org.citrusframework.spi.ReferenceResolver;
import org.citrusframework.spi.ReferenceResolverAware;
import org.citrusframework.spi.Resources;
Expand All @@ -42,6 +45,7 @@
import org.citrusframework.testcontainers.postgresql.StartPostgreSQLAction;
import org.citrusframework.testcontainers.redpanda.StartRedpandaAction;
import org.citrusframework.util.ObjectHelper;
import org.citrusframework.util.StringUtils;

@XmlRootElement(name = "start")
public class Start extends AbstractTestcontainersAction.Builder<StartTestcontainersAction<?>, Start> implements ReferenceResolverAware {
Expand Down Expand Up @@ -197,6 +201,20 @@ private void configureStartActionBuilder(StartTestcontainersAction.AbstractBuild
container.getLabels().getLabels().forEach(label -> builder.withLabel(label.getName(), label.getValue()));
}

if (container.getWaitFor() != null) {
if (container.getWaitFor().isDisabled()) {
builder.waitStrategyDisabled();
} else if (StringUtils.hasText(container.getWaitFor().getLogMessage())) {
builder.waitFor(container.getWaitFor().getLogMessage());
} else if (StringUtils.hasText(container.getWaitFor().getUrl())) {
try {
builder.waitFor(new URL(container.getWaitFor().getUrl()));
} catch (MalformedURLException e) {
throw new CitrusRuntimeException("Invalid Http(s) URL to wait for: %s".formatted(container.getWaitFor().getUrl()), e);
}
}
}

if (container.getExposedPorts() != null) {
container.getExposedPorts().getPorts().forEach(builder::addExposedPort);
}
Expand All @@ -217,6 +235,7 @@ private void configureStartActionBuilder(StartTestcontainersAction.AbstractBuild
"environmentVariables",
"exposedPorts",
"portBindings",
"waitFor",
"volumeMounts"
})
public static class Container {
Expand Down Expand Up @@ -245,6 +264,9 @@ public static class Container {
@XmlElement
protected Labels labels;

@XmlElement(name = "wait-for")
protected WaitFor waitFor;

@XmlElement(name = "exposed-ports")
protected ExposedPorts exposedPorts;

Expand Down Expand Up @@ -318,6 +340,14 @@ public void setLabels(Labels labels) {
this.labels = labels;
}

public void setWaitFor(WaitFor waitFor) {
this.waitFor = waitFor;
}

public WaitFor getWaitFor() {
return waitFor;
}

public ExposedPorts getExposedPorts() {
return exposedPorts;
}
Expand Down Expand Up @@ -389,6 +419,44 @@ public void setMountPath(String mountPath) {
}
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
public static class WaitFor {

@XmlAttribute(name = "log-message")
private String logMessage;

@XmlAttribute
private String url;

@XmlAttribute
private boolean disabled;

public String getLogMessage() {
return logMessage;
}

public void setLogMessage(String logMessage) {
this.logMessage = logMessage;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public boolean isDisabled() {
return disabled;
}

public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"ports"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

package org.citrusframework.testcontainers.yaml;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.citrusframework.TestActor;
import org.citrusframework.exceptions.CitrusRuntimeException;
import org.citrusframework.spi.ReferenceResolver;
import org.citrusframework.spi.ReferenceResolverAware;
import org.citrusframework.spi.Resources;
Expand All @@ -34,6 +37,7 @@
import org.citrusframework.testcontainers.postgresql.StartPostgreSQLAction;
import org.citrusframework.testcontainers.redpanda.StartRedpandaAction;
import org.citrusframework.util.ObjectHelper;
import org.citrusframework.util.StringUtils;

public class Start extends AbstractTestcontainersAction.Builder<StartTestcontainersAction<?>, Start> implements ReferenceResolverAware {

Expand Down Expand Up @@ -174,6 +178,20 @@ private void configureStartActionBuilder(StartTestcontainersAction.AbstractBuild

container.getLabels().forEach(label -> builder.withLabel(label.getName(), label.getValue()));

if (container.getWaitFor() != null) {
if (container.getWaitFor().isDisabled()) {
builder.waitStrategyDisabled();
} else if (StringUtils.hasText(container.getWaitFor().getLogMessage())) {
builder.waitFor(container.getWaitFor().getLogMessage());
} else if (StringUtils.hasText(container.getWaitFor().getUrl())) {
try {
builder.waitFor(new URL(container.getWaitFor().getUrl()));
} catch (MalformedURLException e) {
throw new CitrusRuntimeException("Invalid Http(s) URL to wait for: %s".formatted(container.getWaitFor().getUrl()), e);
}
}
}

container.getExposedPorts().forEach(builder::addExposedPort);

container.getPortBindings().forEach(builder::addPortBinding);
Expand All @@ -200,6 +218,8 @@ public static class Container {

protected List<Label> labels;

protected WaitFor waitFor;

protected List<Integer> exposedPorts;

protected List<String> portBindings;
Expand Down Expand Up @@ -276,6 +296,14 @@ public List<Label> getLabels() {
return labels;
}

public void setWaitFor(WaitFor waitFor) {
this.waitFor = waitFor;
}

public WaitFor getWaitFor() {
return waitFor;
}

public List<Integer> getExposedPorts() {
if (exposedPorts == null) {
exposedPorts = new ArrayList<>();
Expand Down Expand Up @@ -472,6 +500,39 @@ public void setValue(String value) {
}
}

public static class WaitFor {

private String logMessage;

private String url;

private boolean disabled;

public String getLogMessage() {
return logMessage;
}

public void setLogMessage(String logMessage) {
this.logMessage = logMessage;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public boolean isDisabled() {
return disabled;
}

public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
}

public static class VolumeMount {

protected String file;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1962,6 +1962,13 @@
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="wait-for" minOccurs="0">
<xs:complexType>
<xs:attribute name="log-message" type="xs:string"/>
<xs:attribute name="url" type="xs:string"/>
<xs:attribute name="disabled" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<xs:element name="volume-mounts" minOccurs="0">
<xs:complexType>
<xs:sequence>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1962,6 +1962,13 @@
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="wait-for" minOccurs="0">
<xs:complexType>
<xs:attribute name="log-message" type="xs:string"/>
<xs:attribute name="url" type="xs:string"/>
<xs:attribute name="disabled" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<xs:element name="volume-mounts" minOccurs="0">
<xs:complexType>
<xs:sequence>
Expand Down

0 comments on commit 7c6c7a7

Please sign in to comment.