Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions docs/modules/toxiproxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ A Toxiproxy container can be placed in between test code and a container, or in
In either scenario, it is necessary to create a `ToxiproxyContainer` instance on the same Docker network, as follows:

<!--codeinclude-->
[Creating a Toxiproxy container](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:creatingProxy
[Creating a Toxiproxy container](../../modules/toxiproxy/src/test/java/org/testcontainers/toxiproxy/ToxiproxyContainerTest.java) inside_block:creatingProxy
<!--/codeinclude-->

Next, it is necessary to instruct Toxiproxy to start proxying connections.
Expand All @@ -26,13 +26,13 @@ Each `ToxiproxyContainer` can proxy to many target containers if necessary.
We do this as follows:

<!--codeinclude-->
[Starting proxying connections to a target container](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxyObject
[Starting proxying connections to a target container](../../modules/toxiproxy/src/test/java/org/testcontainers/toxiproxy/ToxiproxyContainerTest.java) inside_block:obtainProxyObject
<!--/codeinclude-->

To establish a connection from the test code (on the host machine) to the target container via Toxiproxy, we obtain **Toxiproxy's** proxy host IP and port:

<!--codeinclude-->
[Obtaining proxied host and port](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxiedHostAndPortForHostMachine
[Obtaining proxied host and port](../../modules/toxiproxy/src/test/java/org/testcontainers/toxiproxy/ToxiproxyContainerTest.java) inside_block:obtainProxiedHostAndPortForHostMachine
<!--/codeinclude-->

Code under test should connect to this proxied host IP and port.
Expand All @@ -56,13 +56,13 @@ Please see the [Toxiproxy documentation](https://github.com/Shopify/toxiproxy#to
As one example, we can introduce latency and random jitter to proxied connections as follows:

<!--codeinclude-->
[Adding latency to a connection](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:addingLatency
[Adding latency to a connection](../../modules/toxiproxy/src/test/java/org/testcontainers/toxiproxy/ToxiproxyContainerTest.java) inside_block:addingLatency
<!--/codeinclude-->

Additionally we can disable the proxy to simulate a complete interruption to the network connection:

<!--codeinclude-->
[Cutting a connection](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:disableProxy
[Cutting a connection](../../modules/toxiproxy/src/test/java/org/testcontainers/toxiproxy/ToxiproxyContainerTest.java) inside_block:disableProxy
<!--/codeinclude-->

## Adding this module to your project dependencies
Expand All @@ -86,5 +86,3 @@ Add the following dependency to your `pom.xml`/`build.gradle` file:
## Acknowledgements

This module was inspired by a [hotels.com blog post](https://medium.com/hotels-com-technology/i-dont-know-about-resilience-testing-and-so-can-you-b3c59d80012d).

[toxiproxy-java](https://github.com/trekawek/toxiproxy-java) is used to help control failure conditions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
* <li>HTTP: 8474</li>
* <li>Proxied Ports: 8666-8697</li>
* </ul>
*
* @deprecated use {@link org.testcontainers.toxiproxy.ToxiproxyContainer} instead.
*/
@Deprecated
public class ToxiproxyContainer extends GenericContainer<ToxiproxyContainer> {

private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("shopify/toxiproxy");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.testcontainers.toxiproxy;

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.utility.DockerImageName;

/**
* Testcontainers implementation for Toxiproxy.
* <p>
* Supported images: {@code ghcr.io/shopify/toxiproxy}, {@code shopify/toxiproxy}
* <p>
* Exposed ports:
* <ul>
* <li>HTTP: 8474</li>
* <li>Proxied Ports: 8666-8697</li>
* </ul>
*/
public class ToxiproxyContainer extends GenericContainer<ToxiproxyContainer> {

private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("shopify/toxiproxy");

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;

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);
}
}

/**
* @return Publicly exposed Toxiproxy HTTP API control port.
*/
public int getControlPort() {
return getMappedPort(TOXIPROXY_CONTROL_PORT);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.testcontainers.containers;
package org.testcontainers.toxiproxy;

import eu.rekawek.toxiproxy.Proxy;
import eu.rekawek.toxiproxy.ToxiproxyClient;
import eu.rekawek.toxiproxy.model.ToxicDirection;
import org.junit.jupiter.api.AutoClose;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException;

Expand All @@ -16,7 +18,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;

public class ToxiproxyTest {
public class ToxiproxyContainerTest {

private static final Duration JEDIS_TIMEOUT = Duration.ofSeconds(10);

Expand Down Expand Up @@ -157,33 +159,6 @@ public void testMultipleProxiesCanBeCreated() throws IOException {
}
}

@Test
public void testOriginalAndMappedPorts() {
final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy("hostname", 7070);

final int portViaToxiproxy = proxy.getOriginalProxyPort();
assertThat(portViaToxiproxy).as("original port is correct").isEqualTo(8666);

final ToxiproxyContainer.ContainerProxy proxy1 = toxiproxy.getProxy("hostname1", 8080);
assertThat(proxy1.getOriginalProxyPort()).as("original port is correct").isEqualTo(8667);
assertThat(proxy1.getProxyPort())
.as("mapped port is correct")
.isEqualTo(toxiproxy.getMappedPort(proxy1.getOriginalProxyPort()));

final ToxiproxyContainer.ContainerProxy proxy2 = toxiproxy.getProxy("hostname2", 9090);
assertThat(proxy2.getOriginalProxyPort()).as("original port is correct").isEqualTo(8668);
assertThat(proxy2.getProxyPort())
.as("mapped port is correct")
.isEqualTo(toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()));
}

@Test
public void testProxyName() {
final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy("hostname", 7070);

assertThat(proxy.getName()).as("proxy name is hostname and port").isEqualTo("hostname:7070");
}

private void checkCallWithLatency(
Jedis jedis,
final String description,
Expand Down
Loading