Skip to content
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

Improve ToxiProxyContainer test and docs #6065

Merged
merged 2 commits into from Nov 4, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 3 additions & 6 deletions docs/modules/toxiproxy.md
Expand Up @@ -32,16 +32,13 @@ We do this as follows:
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 for connections from the host machine](../../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/containers/ToxiproxyTest.java) inside_block:obtainProxiedHostAndPortForHostMachine
<!--/codeinclude-->

Code under test should connect to this proxied host IP and port.

To establish a connection from a different container on the same network to the target container via Toxiproxy, we use **Toxiproxy's** network alias and original port:

<!--codeinclude-->
[Obtaining proxied host and port for connections from a different container](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxiedHostAndPortForDifferentContainer
<!--/codeinclude-->
!!! note
Currently, `ToxiProxyContainer` will reserve 31 ports, starting at 8666.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so opinionated 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just documenting for existing support and avoiding breaking changes.


Other containers should connect to this proxied host and port.

Expand Down
Expand Up @@ -85,7 +85,9 @@ public int getControlPort() {
* @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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add to the Javadoc the deprecation comment, to use the Toxiproxy client directly instead?

public ContainerProxy getProxy(GenericContainer<?> container, int port) {
return this.getProxy(container.getNetworkAliases().get(0), port);
}
Expand All @@ -102,7 +104,9 @@ public ContainerProxy getProxy(GenericContainer<?> container, int port) {
* @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;

Expand All @@ -126,6 +130,7 @@ public ContainerProxy getProxy(String hostname, int port) {
}

@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
@Deprecated
public static class ContainerProxy {

private static final String CUT_CONNECTION_DOWNSTREAM = "CUT_CONNECTION_DOWNSTREAM";
Expand Down
@@ -1,9 +1,10 @@
package org.testcontainers.containers;

import eu.rekawek.toxiproxy.Proxy;
import eu.rekawek.toxiproxy.ToxiproxyClient;
import eu.rekawek.toxiproxy.model.ToxicDirection;
import org.junit.Rule;
import org.junit.Test;
import org.testcontainers.utility.DockerImageName;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException;

Expand All @@ -19,30 +20,21 @@ public class ToxiproxyTest {

// spotless:off
// creatingProxy {
// An alias that can be used to resolve the Toxiproxy container by name in the network it is connected to.
// It can be used as a hostname of the Toxiproxy container by other containers in the same network.
private static final String TOXIPROXY_NETWORK_ALIAS = "toxiproxy";

// Create a common docker network so that containers can communicate
@Rule
public Network network = Network.newNetwork();

private static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:5.0.4");

// The target container - this could be anything
@Rule
public GenericContainer<?> redis = new GenericContainer<>(REDIS_IMAGE)
public GenericContainer<?> redis = new GenericContainer<>("redis:5.0.4")
.withExposedPorts(6379)
.withNetwork(network);

private static final DockerImageName TOXIPROXY_IMAGE = DockerImageName.parse("ghcr.io/shopify/toxiproxy:2.5.0");
.withNetwork(network)
.withNetworkAliases("redis");

// Toxiproxy container, which will be used as a TCP proxy
@Rule
public ToxiproxyContainer toxiproxy = new ToxiproxyContainer(TOXIPROXY_IMAGE)
.withNetwork(network)
.withNetworkAliases(TOXIPROXY_NETWORK_ALIAS);

public ToxiproxyContainer toxiproxy = new ToxiproxyContainer("ghcr.io/shopify/toxiproxy:2.5.0")
.withNetwork(network);
// }
// spotless:on

Expand All @@ -58,12 +50,13 @@ public void testDirect() {
@Test
public void testLatencyViaProxy() throws IOException {
// obtainProxyObject {
final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379);
final ToxiproxyClient toxiproxyClient = new ToxiproxyClient(toxiproxy.getHost(), toxiproxy.getControlPort());
final Proxy proxy = toxiproxyClient.createProxy("redis", "0.0.0.0:8666", "redis:6379");
// }

// obtainProxiedHostAndPortForHostMachine {
final String ipAddressViaToxiproxy = proxy.getContainerIpAddress();
final int portViaToxiproxy = proxy.getProxyPort();
final String ipAddressViaToxiproxy = toxiproxy.getHost();
final int portViaToxiproxy = toxiproxy.getMappedPort(8666);
// }

final Jedis jedis = createJedis(ipAddressViaToxiproxy, portViaToxiproxy);
Expand All @@ -84,17 +77,19 @@ public void testLatencyViaProxy() throws IOException {
}

@Test
public void testConnectionCut() {
final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379);
final Jedis jedis = createJedis(proxy.getContainerIpAddress(), proxy.getProxyPort());
public void testConnectionCut() throws IOException {
final ToxiproxyClient toxiproxyClient = new ToxiproxyClient(toxiproxy.getHost(), toxiproxy.getControlPort());
final Proxy proxy = toxiproxyClient.createProxy("redis", "0.0.0.0:8666", "redis:6379");
final Jedis jedis = createJedis(toxiproxy.getHost(), toxiproxy.getMappedPort(8666));
jedis.set("somekey", "somevalue");

assertThat(jedis.get("somekey"))
.as("access to the container works OK before cutting the connection")
.isEqualTo("somevalue");

// disableProxy {
proxy.setConnectionCut(true);
proxy.toxics().bandwidth("CUT_CONNECTION_DOWNSTREAM", ToxicDirection.DOWNSTREAM, 0);
proxy.toxics().bandwidth("CUT_CONNECTION_UPSTREAM", ToxicDirection.UPSTREAM, 0);

// for example, expect failure when the connection is cut
assertThat(
Expand All @@ -105,7 +100,8 @@ public void testConnectionCut() {
.as("calls fail when the connection is cut")
.isInstanceOf(JedisConnectionException.class);

proxy.setConnectionCut(false);
proxy.toxics().get("CUT_CONNECTION_DOWNSTREAM").remove();
proxy.toxics().get("CUT_CONNECTION_UPSTREAM").remove();

// and with the connection re-established, expect success
assertThat(jedis.get("somekey"))
Expand All @@ -115,24 +111,30 @@ public void testConnectionCut() {
}

@Test
public void testMultipleProxiesCanBeCreated() {
public void testMultipleProxiesCanBeCreated() throws IOException {
try (
GenericContainer<?> secondRedis = new GenericContainer<>(REDIS_IMAGE)
GenericContainer<?> secondRedis = new GenericContainer<>("redis:5.0.4")
.withExposedPorts(6379)
.withNetwork(network)
.withNetworkAliases("redis2")
) {
secondRedis.start();

final ToxiproxyContainer.ContainerProxy firstProxy = toxiproxy.getProxy(redis, 6379);
final ToxiproxyContainer.ContainerProxy secondProxy = toxiproxy.getProxy(secondRedis, 6379);
final ToxiproxyClient toxiproxyClient = new ToxiproxyClient(
toxiproxy.getHost(),
toxiproxy.getControlPort()
);
final Proxy firstProxy = toxiproxyClient.createProxy("redis1", "0.0.0.0:8666", "redis:6379");
toxiproxyClient.createProxy("redis2", "0.0.0.0:8667", "redis2:6379");

final Jedis firstJedis = createJedis(firstProxy.getContainerIpAddress(), firstProxy.getProxyPort());
final Jedis secondJedis = createJedis(secondProxy.getContainerIpAddress(), secondProxy.getProxyPort());
final Jedis firstJedis = createJedis(toxiproxy.getHost(), toxiproxy.getMappedPort(8666));
final Jedis secondJedis = createJedis(toxiproxy.getHost(), toxiproxy.getMappedPort(8667));

firstJedis.set("somekey", "somevalue");
secondJedis.set("somekey", "somevalue");

firstProxy.setConnectionCut(true);
firstProxy.toxics().bandwidth("CUT_CONNECTION_DOWNSTREAM", ToxicDirection.DOWNSTREAM, 0);
firstProxy.toxics().bandwidth("CUT_CONNECTION_UPSTREAM", ToxicDirection.UPSTREAM, 0);

assertThat(
catchThrowable(() -> {
Expand All @@ -149,11 +151,8 @@ public void testMultipleProxiesCanBeCreated() {
@Test
public void testOriginalAndMappedPorts() {
final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy("hostname", 7070);
// obtainProxiedHostAndPortForDifferentContainer {
final String hostViaToxiproxy = TOXIPROXY_NETWORK_ALIAS;

final int portViaToxiproxy = proxy.getOriginalProxyPort();
// }
assertThat(hostViaToxiproxy).as("host is correct").isEqualTo(TOXIPROXY_NETWORK_ALIAS);
assertThat(portViaToxiproxy).as("original port is correct").isEqualTo(8666);

final ToxiproxyContainer.ContainerProxy proxy1 = toxiproxy.getProxy("hostname1", 8080);
Expand All @@ -176,13 +175,6 @@ public void testProxyName() {
assertThat(proxy.getName()).as("proxy name is hostname and port").isEqualTo("hostname:7070");
}

@Test
public void testControlPort() {
final int controlPort = toxiproxy.getControlPort();

assertThat(controlPort).as("control port is mapped from port 8474").isEqualTo(toxiproxy.getMappedPort(8474));
}

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