0
# Testcontainers Toxiproxy
1
2
Testcontainers module for Shopify's Toxiproxy TCP proxy that simulates network failure conditions for resilience testing. This module enables developers to test application resilience by introducing network toxics (failures) between containers or between test code and containers in isolated test environments.
3
4
## Package Information
5
6
- **Package Name**: org.testcontainers:toxiproxy
7
- **Package Type**: Maven
8
- **Language**: Java
9
- **Installation**:
10
```xml
11
<dependency>
12
<groupId>org.testcontainers</groupId>
13
<artifactId>toxiproxy</artifactId>
14
<version>1.21.3</version>
15
<scope>test</scope>
16
</dependency>
17
```
18
19
Gradle:
20
```groovy
21
testImplementation 'org.testcontainers:toxiproxy:1.21.3'
22
```
23
24
## Core Imports
25
26
```java
27
import org.testcontainers.containers.ToxiproxyContainer;
28
import eu.rekawek.toxiproxy.ToxiproxyClient;
29
import eu.rekawek.toxiproxy.Proxy;
30
import eu.rekawek.toxiproxy.model.ToxicDirection;
31
```
32
33
## Basic Usage
34
35
```java
36
import org.testcontainers.containers.ToxiproxyContainer;
37
import org.testcontainers.containers.GenericContainer;
38
import org.testcontainers.containers.Network;
39
import eu.rekawek.toxiproxy.ToxiproxyClient;
40
import eu.rekawek.toxiproxy.Proxy;
41
import eu.rekawek.toxiproxy.model.ToxicDirection;
42
43
// Create a common docker network
44
Network network = Network.newNetwork();
45
46
// Target container (could be any service)
47
GenericContainer<?> redis = new GenericContainer<>("redis:6-alpine")
48
.withExposedPorts(6379)
49
.withNetwork(network)
50
.withNetworkAliases("redis");
51
52
// Toxiproxy container on same network
53
ToxiproxyContainer toxiproxy = new ToxiproxyContainer("ghcr.io/shopify/toxiproxy:2.5.0")
54
.withNetwork(network);
55
56
// Start containers
57
redis.start();
58
toxiproxy.start();
59
60
// Create Toxiproxy client and proxy
61
ToxiproxyClient client = new ToxiproxyClient(
62
toxiproxy.getHost(),
63
toxiproxy.getControlPort()
64
);
65
Proxy proxy = client.createProxy("redis", "0.0.0.0:8666", "redis:6379");
66
67
// Connect to service via proxy
68
String proxyHost = toxiproxy.getHost();
69
int proxyPort = toxiproxy.getMappedPort(8666);
70
71
// Apply network toxics for testing
72
proxy.toxics().latency("latency", ToxicDirection.DOWNSTREAM, 1000);
73
proxy.toxics().bandwidth("slow_connection", ToxicDirection.UPSTREAM, 100);
74
```
75
76
## Architecture
77
78
The Toxiproxy module is built around these key components:
79
80
- **ToxiproxyContainer**: Main container wrapper that manages the Toxiproxy Docker container lifecycle
81
- **ToxiproxyClient**: Client from toxiproxy-java library for creating and managing proxies via HTTP API
82
- **Proxy**: Represents individual proxy connections with toxic management capabilities
83
- **ContainerProxy**: Deprecated wrapper class providing convenience methods for proxy management
84
- **Network Integration**: Requires containers to be on same Docker network for inter-container communication
85
86
## Capabilities
87
88
### Container Management
89
90
Create and manage the Toxiproxy Docker container with automatic port exposure and lifecycle management.
91
92
```java { .api }
93
/**
94
* Testcontainers implementation for Toxiproxy.
95
* Supported images: ghcr.io/shopify/toxiproxy, shopify/toxiproxy
96
* Exposed ports: HTTP: 8474, Proxied Ports: 8666-8697
97
*/
98
public class ToxiproxyContainer extends GenericContainer<ToxiproxyContainer> {
99
100
/**
101
* @deprecated use ToxiproxyContainer(DockerImageName) instead
102
*/
103
@Deprecated
104
public ToxiproxyContainer();
105
106
public ToxiproxyContainer(String dockerImageName);
107
108
public ToxiproxyContainer(DockerImageName dockerImageName);
109
110
/**
111
* @return Publicly exposed Toxiproxy HTTP API control port
112
*/
113
public int getControlPort();
114
}
115
```
116
117
**Usage Examples:**
118
119
```java
120
// Using default image
121
ToxiproxyContainer toxiproxy = new ToxiproxyContainer();
122
123
// Using specific image version
124
ToxiproxyContainer toxiproxy = new ToxiproxyContainer("ghcr.io/shopify/toxiproxy:2.5.0");
125
126
// Using DockerImageName
127
DockerImageName imageName = DockerImageName.parse("shopify/toxiproxy:2.1.0");
128
ToxiproxyContainer toxiproxy = new ToxiproxyContainer(imageName);
129
130
// Get control port for client creation
131
int controlPort = toxiproxy.getControlPort();
132
```
133
134
### Proxy Creation and Management
135
136
Create TCP proxies between services and apply network failure conditions using the ToxiproxyClient.
137
138
```java { .api }
139
/**
140
* Client for managing Toxiproxy proxies via HTTP API
141
*/
142
public class ToxiproxyClient {
143
public ToxiproxyClient(String host, int port);
144
145
/**
146
* Create a new proxy
147
* @param name proxy name (must be unique)
148
* @param listen listen address in format "host:port"
149
* @param upstream upstream address in format "host:port"
150
* @return Proxy instance for managing toxics
151
*/
152
public Proxy createProxy(String name, String listen, String upstream) throws IOException;
153
}
154
155
/**
156
* Represents a single proxy connection
157
*/
158
public class Proxy {
159
/**
160
* @return proxy name
161
*/
162
public String getName();
163
164
/**
165
* @return ToxicList for managing network failure conditions
166
*/
167
public ToxicList toxics();
168
}
169
```
170
171
**Usage Examples:**
172
173
```java
174
// Create client
175
ToxiproxyClient client = new ToxiproxyClient(toxiproxy.getHost(), toxiproxy.getControlPort());
176
177
// Create proxy for Redis container
178
Proxy redisProxy = client.createProxy("redis", "0.0.0.0:8666", "redis:6379");
179
180
// Create proxy for external service
181
Proxy apiProxy = client.createProxy("api", "0.0.0.0:8667", "api.example.com:80");
182
183
// Get proxy details
184
String proxyName = redisProxy.getName(); // "redis"
185
```
186
187
### Network Toxic Management
188
189
Apply various network failure conditions to test application resilience.
190
191
```java { .api }
192
/**
193
* Interface for managing toxic conditions on a proxy
194
*/
195
public interface ToxicList {
196
/**
197
* Limit connection bandwidth
198
* @param name toxic name
199
* @param direction traffic direction (UPSTREAM/DOWNSTREAM)
200
* @param rate maximum kilobytes per second
201
* @return Toxic instance
202
*/
203
Toxic bandwidth(String name, ToxicDirection direction, long rate);
204
205
/**
206
* Add latency with optional jitter
207
* @param name toxic name
208
* @param direction traffic direction
209
* @param latency latency in milliseconds
210
* @return Toxic instance for further configuration
211
*/
212
Toxic latency(String name, ToxicDirection direction, long latency);
213
214
/**
215
* Slice TCP data into small packets
216
* @param name toxic name
217
* @param direction traffic direction
218
* @param averageSize average packet size in bytes
219
* @param sizeVariation size variation in bytes
220
* @param delay delay between packets in microseconds
221
* @return Toxic instance
222
*/
223
Toxic slicer(String name, ToxicDirection direction, int averageSize, int sizeVariation, int delay);
224
225
/**
226
* Delay socket closing
227
* @param name toxic name
228
* @param direction traffic direction
229
* @param delay delay in milliseconds
230
* @return Toxic instance
231
*/
232
Toxic slowClose(String name, ToxicDirection direction, long delay);
233
234
/**
235
* Stop data transmission and close connection after timeout
236
* @param name toxic name
237
* @param direction traffic direction
238
* @param timeout timeout in milliseconds (0 = never close)
239
* @return Toxic instance
240
*/
241
Toxic timeout(String name, ToxicDirection direction, long timeout);
242
243
/**
244
* Close connection when data limit exceeded
245
* @param name toxic name
246
* @param direction traffic direction
247
* @param bytes data limit in bytes
248
* @return Toxic instance
249
*/
250
Toxic limitData(String name, ToxicDirection direction, long bytes);
251
252
/**
253
* Get existing toxic by name
254
* @param name toxic name
255
* @return Toxic instance
256
*/
257
Toxic get(String name);
258
}
259
260
/**
261
* Represents an individual toxic condition
262
*/
263
public interface Toxic {
264
/**
265
* Remove this toxic
266
*/
267
void remove() throws IOException;
268
269
/**
270
* Set jitter for latency toxics
271
* @param jitter jitter in milliseconds
272
* @return this toxic for chaining
273
*/
274
Toxic setJitter(int jitter);
275
}
276
277
/**
278
* Traffic direction for toxic application
279
*/
280
public enum ToxicDirection {
281
UPSTREAM, // Client to server
282
DOWNSTREAM // Server to client
283
}
284
```
285
286
**Usage Examples:**
287
288
```java
289
// Add 1 second latency with 100ms jitter to downstream traffic
290
proxy.toxics()
291
.latency("latency", ToxicDirection.DOWNSTREAM, 1000)
292
.setJitter(100);
293
294
// Limit bandwidth to 100 KB/s upstream
295
proxy.toxics().bandwidth("slow_upload", ToxicDirection.UPSTREAM, 100);
296
297
// Completely cut connection (0 bandwidth both directions)
298
proxy.toxics().bandwidth("cut_down", ToxicDirection.DOWNSTREAM, 0);
299
proxy.toxics().bandwidth("cut_up", ToxicDirection.UPSTREAM, 0);
300
301
// Add connection timeout after 5 seconds
302
proxy.toxics().timeout("connection_timeout", ToxicDirection.DOWNSTREAM, 5000);
303
304
// Slice packets into 32-byte chunks with 10ms delay
305
proxy.toxics().slicer("packet_slicer", ToxicDirection.DOWNSTREAM, 32, 8, 10000);
306
307
// Remove a toxic
308
proxy.toxics().get("latency").remove();
309
```
310
311
### Legacy Proxy Management (Deprecated)
312
313
Legacy convenience methods for proxy creation and management. These methods are deprecated and will be removed in future versions.
314
315
```java { .api }
316
public class ToxiproxyContainer extends GenericContainer<ToxiproxyContainer> {
317
318
/**
319
* @deprecated ToxiproxyContainer will not build the client. Proxies should be provided manually.
320
*/
321
@Deprecated
322
public ContainerProxy getProxy(GenericContainer<?> container, int port);
323
324
/**
325
* @deprecated ToxiproxyContainer will not build the client. Proxies should be provided manually.
326
*/
327
@Deprecated
328
public ContainerProxy getProxy(String hostname, int port);
329
330
/**
331
* @deprecated Legacy proxy wrapper class
332
*/
333
@Deprecated
334
public static class ContainerProxy {
335
336
/**
337
* The IP address that this proxy container may be reached on from the host machine
338
*/
339
public String getContainerIpAddress();
340
341
/**
342
* The mapped port of this proxy. This is a port of the host machine
343
*/
344
public int getProxyPort();
345
346
/**
347
* The original (exposed) port of this proxy. This is a port of the Toxiproxy Docker container
348
*/
349
public int getOriginalProxyPort();
350
351
public String getName();
352
353
public ToxicList toxics();
354
355
/**
356
* Cuts the connection by setting bandwidth in both directions to zero
357
* @param shouldCutConnection true to cut connection, false to restore
358
*/
359
public void setConnectionCut(boolean shouldCutConnection);
360
}
361
}
362
```
363
364
## Error Handling
365
366
The module may throw these exceptions during operation:
367
368
```java { .api }
369
/**
370
* Common exceptions
371
*/
372
IOException // Thrown during proxy creation or toxic manipulation
373
IllegalStateException // Thrown when maximum proxy limit (32) exceeded
374
RuntimeException // Wrapper for IOException in proxy operations
375
```
376
377
**Error Handling Examples:**
378
379
```java
380
try {
381
Proxy proxy = client.createProxy("service", "0.0.0.0:8666", "service:8080");
382
proxy.toxics().latency("test_latency", ToxicDirection.DOWNSTREAM, 1000);
383
} catch (IOException e) {
384
// Handle proxy creation or toxic application failure
385
System.err.println("Failed to configure proxy: " + e.getMessage());
386
}
387
388
try {
389
// This will fail if more than 32 proxies are created
390
ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy("host", 8080);
391
} catch (IllegalStateException e) {
392
System.err.println("Maximum number of proxies exceeded");
393
}
394
```