1 package de.juplo.kafka.chat.backend;
3 import lombok.extern.slf4j.Slf4j;
4 import org.awaitility.Awaitility;
5 import org.springframework.http.HttpStatus;
6 import org.springframework.web.reactive.function.client.WebClient;
7 import org.testcontainers.containers.BindMode;
8 import org.testcontainers.containers.GenericContainer;
9 import org.testcontainers.containers.Network;
10 import org.testcontainers.containers.output.Slf4jLogConsumer;
11 import org.testcontainers.containers.wait.strategy.Wait;
12 import org.testcontainers.images.ImagePullPolicy;
13 import org.testcontainers.utility.DockerImageName;
14 import reactor.core.publisher.Mono;
16 import java.time.Duration;
17 import java.util.Arrays;
21 public abstract class AbstractHandoverITContainers
23 static final ImagePullPolicy NEVER_PULL = imageName -> false;
26 final Network network = Network.newNetwork();
27 final GenericContainer haproxy, backend1, backend2, backend3;
30 AbstractHandoverITContainers()
32 haproxy = createHaproxyContainer();
33 backend1 = createBackendContainer("1");
34 backend2 = createBackendContainer("2");
35 backend3 = createBackendContainer("3");
39 void setUpExtra() throws Exception
41 log.info("This setup does not need any extra containers");
44 void setUp() throws Exception
51 GenericContainer backend,
52 TestWriter[] testWriters)
56 int[] numSentMessages = Arrays
58 .mapToInt(testWriter -> testWriter.getNumSentMessages())
63 .atMost(Duration.ofSeconds(30))
64 .until(() -> WebClient
65 .create("http://localhost:" + backend.getMappedPort(8080))
67 .uri("/actuator/health")
68 .exchangeToMono(response ->
70 if (response.statusCode().equals(HttpStatus.OK))
73 .bodyToMono(StatusTo.class)
74 .map(StatusTo::getStatus)
75 .map(status -> status.equalsIgnoreCase("UP"));
79 return Mono.just(false);
86 .killContainerCmd(haproxy.getContainerId())
92 .atMost(Duration.ofSeconds(30))
93 .until(() -> WebClient
94 .create("http://localhost:" + haproxy.getMappedPort(8400))
96 .uri("/actuator/health")
97 .exchangeToMono(response ->
99 if (response.statusCode().equals(HttpStatus.OK))
102 .bodyToMono(StatusTo.class)
103 .map(StatusTo::getStatus)
104 .map(status -> status.equalsIgnoreCase("UP"));
108 return Mono.just(false);
115 .atMost(Duration.ofSeconds(30))
118 for (int i = 0; i < testWriters.length; i++)
120 TestWriter testWriter = testWriters[i];
121 int sentTotal = testWriter.getNumSentMessages();
122 if (sentTotal == numSentMessages[i])
125 "No progress for {}: sent-before={}, sent-total={}",
137 abstract String[] getBackendCommand();
139 final GenericContainer createHaproxyContainer()
141 return new GenericContainer(DockerImageName.parse("haproxytech/haproxy-debian:2.8"))
142 .withNetwork(network)
143 .withNetworkAliases("haproxy")
144 .withClasspathResourceMapping(
146 "/usr/local/etc/haproxy/haproxy.cfg",
148 .withClasspathResourceMapping(
150 "/usr/local/etc/haproxy/sharding.map",
152 .withExposedPorts(8400, 8401, 8404)
153 .withLogConsumer(new Slf4jLogConsumer(log, true).withPrefix("HAPROXY"));
156 final GenericContainer createBackendContainer(String id)
158 return new GenericContainer(DockerImageName.parse("juplo/chat-backend:0.0.1-SNAPSHOT"))
159 .withImagePullPolicy(NEVER_PULL)
160 .withNetwork(network)
161 .withNetworkAliases("backend-ID".replaceAll("ID", id))
162 .withCommand(Arrays.stream(getBackendCommand())
163 .map(commandPart -> commandPart.replaceAll("ID", id))
164 .toArray(size -> new String[size]))
165 .withExposedPorts(8080)
166 .waitingFor(Wait.forLogMessage(".*Started\\ ChatBackendApplication.*\\n", 1))
167 .withLogConsumer(new Slf4jLogConsumer(
171 .withPrefix("BACKEND-ID".replaceAll("ID", id)));