org.testcontainers.containers.ToxiproxyContainer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of toxiproxy Show documentation
Show all versions of toxiproxy Show documentation
Isolated container management for Java code testing
The newest version!
package org.testcontainers.containers;
import com.github.dockerjava.api.command.InspectContainerResponse;
import eu.rekawek.toxiproxy.Proxy;
import eu.rekawek.toxiproxy.ToxiproxyClient;
import eu.rekawek.toxiproxy.model.ToxicDirection;
import eu.rekawek.toxiproxy.model.ToxicList;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.utility.DockerImageName;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Testcontainers implementation for Toxiproxy.
*
* Supported images: {@code ghcr.io/shopify/toxiproxy}, {@code shopify/toxiproxy}
*
* Exposed ports:
*
* - HTTP: 8474
* - Proxied Ports: 8666-8697
*
*/
public class ToxiproxyContainer extends GenericContainer {
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("shopify/toxiproxy");
private static final String DEFAULT_TAG = "2.1.0";
private static final DockerImageName GHCR_IMAGE_NAME = DockerImageName.parse("ghcr.io/shopify/toxiproxy");
private static final int TOXIPROXY_CONTROL_PORT = 8474;
private static final int FIRST_PROXIED_PORT = 8666;
private static final int LAST_PROXIED_PORT = 8666 + 31;
private ToxiproxyClient client;
private final Map proxies = new HashMap<>();
private final AtomicInteger nextPort = new AtomicInteger(FIRST_PROXIED_PORT);
/**
* @deprecated use {@link #ToxiproxyContainer(DockerImageName)} instead
*/
@Deprecated
public ToxiproxyContainer() {
this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG));
}
public ToxiproxyContainer(String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
}
public ToxiproxyContainer(final DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, GHCR_IMAGE_NAME);
addExposedPorts(TOXIPROXY_CONTROL_PORT);
setWaitStrategy(new HttpWaitStrategy().forPath("/version").forPort(TOXIPROXY_CONTROL_PORT));
// allow up to 32 ports to be proxied (arbitrary value). Here we make the ports exposed; whether or not
// Toxiproxy will listen is controlled at runtime using getProxy(...)
for (int i = FIRST_PROXIED_PORT; i <= LAST_PROXIED_PORT; i++) {
addExposedPort(i);
}
}
@Override
protected void containerIsStarted(InspectContainerResponse containerInfo) {
client = new ToxiproxyClient(getHost(), getMappedPort(TOXIPROXY_CONTROL_PORT));
}
/**
* @return Publicly exposed Toxiproxy HTTP API control port.
*/
public int getControlPort() {
return getMappedPort(TOXIPROXY_CONTROL_PORT);
}
/**
* Obtain a {@link ContainerProxy} instance for target container that is managed by Testcontainers. The target
* container should be routable from this {@link ToxiproxyContainer} instance (e.g. on the same
* Docker {@link Network}).
*
* @param container target container
* @param port port number on the target service that should be proxied
* @return a {@link ContainerProxy} instance
* @deprecated {@link ToxiproxyContainer} will not build the client. Proxies should be provided manually.
*/
@Deprecated
public ContainerProxy getProxy(GenericContainer> container, int port) {
return this.getProxy(container.getNetworkAliases().get(0), port);
}
/**
* Obtain a {@link ContainerProxy} instance for a specific hostname and port, which can be for any host
* that is routable from this {@link ToxiproxyContainer} instance (e.g. on the same
* Docker {@link Network} or on routable from the Docker host).
*
* It is expected that {@link ToxiproxyContainer#getProxy(GenericContainer, int)} will be more
* useful in most scenarios, but this method is present to allow use of Toxiproxy in front of containers
* or external servers that are not managed by Testcontainers.
*
* @param hostname hostname of target server to be proxied
* @param port port number on the target server that should be proxied
* @return a {@link ContainerProxy} instance
* @deprecated {@link ToxiproxyContainer} will not build the client. Proxies should be provided manually.
*/
@Deprecated
public ContainerProxy getProxy(String hostname, int port) {
String upstream = hostname + ":" + port;
return proxies.computeIfAbsent(
upstream,
__ -> {
try {
final int toxiPort = nextPort.getAndIncrement();
if (toxiPort > LAST_PROXIED_PORT) {
throw new IllegalStateException("Maximum number of proxies exceeded");
}
final Proxy proxy = client.createProxy(upstream, "0.0.0.0:" + toxiPort, upstream);
final int mappedPort = getMappedPort(toxiPort);
return new ContainerProxy(proxy, getHost(), mappedPort, toxiPort);
} catch (IOException e) {
throw new RuntimeException("Proxy could not be created", e);
}
}
);
}
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
@Deprecated
public static class ContainerProxy {
private static final String CUT_CONNECTION_DOWNSTREAM = "CUT_CONNECTION_DOWNSTREAM";
private static final String CUT_CONNECTION_UPSTREAM = "CUT_CONNECTION_UPSTREAM";
private final Proxy toxi;
/**
* The IP address that this proxy container may be reached on from the host machine.
*/
@Getter
private final String containerIpAddress;
/**
* The mapped port of this proxy. This is a port of the host machine. It can be used to
* access the Toxiproxy container from the host machine.
*/
@Getter
private final int proxyPort;
/**
* The original (exposed) port of this proxy. This is a port of the Toxiproxy Docker
* container. It can be used to access this container from a different Docker container
* on the same network.
*/
@Getter
private final int originalProxyPort;
private boolean isCurrentlyCut;
public String getName() {
return toxi.getName();
}
public ToxicList toxics() {
return toxi.toxics();
}
/**
* Cuts the connection by setting bandwidth in both directions to zero.
* @param shouldCutConnection true if the connection should be cut, or false if it should be re-enabled
*/
public void setConnectionCut(boolean shouldCutConnection) {
try {
if (shouldCutConnection) {
toxics().bandwidth(CUT_CONNECTION_DOWNSTREAM, ToxicDirection.DOWNSTREAM, 0);
toxics().bandwidth(CUT_CONNECTION_UPSTREAM, ToxicDirection.UPSTREAM, 0);
isCurrentlyCut = true;
} else if (isCurrentlyCut) {
toxics().get(CUT_CONNECTION_DOWNSTREAM).remove();
toxics().get(CUT_CONNECTION_UPSTREAM).remove();
isCurrentlyCut = false;
}
} catch (IOException e) {
throw new RuntimeException("Could not control proxy", e);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy