diff --git a/server/queue/queue-rabbitmq/pom.xml b/server/queue/queue-rabbitmq/pom.xml index d7d8ad7e8d8..86bbfa2208f 100644 --- a/server/queue/queue-rabbitmq/pom.xml +++ b/server/queue/queue-rabbitmq/pom.xml @@ -53,6 +53,11 @@ com.rabbitmq amqp-client + + commons-codec + commons-codec + test + org.assertj assertj-core diff --git a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerClusterRabbitMQExtention.java b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerClusterRabbitMQExtention.java new file mode 100644 index 00000000000..fae201637f4 --- /dev/null +++ b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerClusterRabbitMQExtention.java @@ -0,0 +1,112 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ +package org.apache.james.queue.rabbitmq; + +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.testcontainers.containers.Network; + +import com.google.common.collect.ImmutableList; + +public class DockerClusterRabbitMQExtention implements BeforeEachCallback, AfterEachCallback, ParameterResolver { + + public static final String RABBIT_1 = "rabbit1"; + public static final String RABBIT_2 = "rabbit2"; + public static final String RABBIT_3 = "rabbit3"; + private DockerRabbitMQCluster cluster; + private Network network; + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + String cookie = DigestUtils.sha1Hex("secret cookie here"); + + network = Network.NetworkImpl.builder() + .enableIpv6(false) + .createNetworkCmdModifiers(ImmutableList.of()) + .build(); + + DockerRabbitMQ rabbitMQ1 = DockerRabbitMQ.withCookieAndNodeName(RABBIT_1, cookie, "rabbit@rabbit1", network); + DockerRabbitMQ rabbitMQ2 = DockerRabbitMQ.withCookieAndNodeName(RABBIT_2, cookie, "rabbit@rabbit2", network); + DockerRabbitMQ rabbitMQ3 = DockerRabbitMQ.withCookieAndNodeName(RABBIT_3, cookie, "rabbit@rabbit3", network); + + rabbitMQ1.start(); + rabbitMQ2.start(); + rabbitMQ3.start(); + + rabbitMQ2.join(rabbitMQ1); + rabbitMQ3.join(rabbitMQ1); + + rabbitMQ2.startApp(); + rabbitMQ3.startApp(); + + cluster = new DockerRabbitMQCluster(rabbitMQ1, rabbitMQ2, rabbitMQ3); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + cluster.stop(); + network.close(); + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return (parameterContext.getParameter().getType() == DockerRabbitMQCluster.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return cluster; + } + + public static class DockerRabbitMQCluster { + + private final DockerRabbitMQ rabbitMQ1; + private final DockerRabbitMQ rabbitMQ2; + private final DockerRabbitMQ rabbitMQ3; + + public DockerRabbitMQCluster(DockerRabbitMQ rabbitMQ1, DockerRabbitMQ rabbitMQ2, DockerRabbitMQ rabbitMQ3) { + this.rabbitMQ1 = rabbitMQ1; + this.rabbitMQ2 = rabbitMQ2; + this.rabbitMQ3 = rabbitMQ3; + } + + public void stop() { + rabbitMQ1.stop(); + rabbitMQ2.stop(); + rabbitMQ3.stop(); + } + + public DockerRabbitMQ getRabbitMQ1() { + return rabbitMQ1; + } + + public DockerRabbitMQ getRabbitMQ2() { + return rabbitMQ2; + } + + public DockerRabbitMQ getRabbitMQ3() { + return rabbitMQ3; + } + } +} diff --git a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerRabbitMQ.java b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerRabbitMQ.java index 8fea5b07eb4..a964b99c9eb 100644 --- a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerRabbitMQ.java +++ b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerRabbitMQ.java @@ -18,30 +18,50 @@ ****************************************************************/ package org.apache.james.queue.rabbitmq; +import java.util.Optional; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; import com.rabbitmq.client.ConnectionFactory; public class DockerRabbitMQ { private static final Logger LOGGER = LoggerFactory.getLogger(DockerRabbitMQ.class); + private static final String DEFAULT_RABBIT_NODE = "my-rabbit"; private static final int DEFAULT_RABBITMQ_PORT = 5672; - private static final String DEFAULT_RABBITMQ_HOSTNAME = "my-rabbit"; private static final String DEFAULT_RABBITMQ_USERNAME = "guest"; private static final String DEFAULT_RABBITMQ_PASSWORD = "guest"; + private static final String RABBITMQ_ERLANG_COOKIE = "RABBITMQ_ERLANG_COOKIE"; + private static final String RABBITMQ_NODENAME = "RABBITMQ_NODENAME"; + + private final GenericContainer container; + private final Optional nodeName; - private GenericContainer container; + public static DockerRabbitMQ withCookieAndNodeName(String hostName, String erlangCookie, String nodeName, Network network) { + return new DockerRabbitMQ(Optional.ofNullable(hostName), Optional.ofNullable(erlangCookie), Optional.ofNullable(nodeName), + Optional.of(network)); + } + + public static DockerRabbitMQ withoutCookie() { + return new DockerRabbitMQ(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); + } @SuppressWarnings("resource") - public DockerRabbitMQ() { + private DockerRabbitMQ(Optional hostName, Optional erlangCookie, Optional nodeName, Optional net) { container = new GenericContainer<>("rabbitmq:3.7.5") - .withCreateContainerCmdModifier(cmd -> cmd.withHostName(DEFAULT_RABBITMQ_HOSTNAME)) + .withCreateContainerCmdModifier(cmd -> cmd.withName(hostName.orElse("localhost"))) + .withCreateContainerCmdModifier(cmd -> cmd.withHostName(hostName.orElse(DEFAULT_RABBIT_NODE))) .withExposedPorts(DEFAULT_RABBITMQ_PORT) .waitingFor(RabbitMQWaitStrategy.withDefaultTimeout(this)) .withLogConsumer(frame -> LOGGER.debug(frame.getUtf8String())); + net.ifPresent(container::withNetwork); + erlangCookie.ifPresent(cookie -> container.withEnv(RABBITMQ_ERLANG_COOKIE, cookie)); + nodeName.ifPresent(name -> container.withEnv(RABBITMQ_NODENAME, name)); + this.nodeName = nodeName; } public String getHostIp() { @@ -81,4 +101,29 @@ public void restart() { DockerClientFactory.instance().client() .restartContainerCmd(container.getContainerId()); } + + public GenericContainer container() { + return container; + } + + public String node() { + return nodeName.get(); + } + + public void join(DockerRabbitMQ rabbitMQ) throws Exception { + container() + .execInContainer("rabbitmqctl", "stop_app") + .getStdout(); + container() + .execInContainer("rabbitmqctl", "join_cluster", rabbitMQ.node()) + .getStdout(); + } + + public void startApp() throws Exception { + String stdout = container() + .execInContainer("rabbitmqctl", "start_app") + .getStdout(); + System.out.println(stdout); + + } } diff --git a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerRabbitMQExtension.java b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerRabbitMQExtension.java index 925ff08bdb6..b0cbfd7aa98 100644 --- a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerRabbitMQExtension.java +++ b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/DockerRabbitMQExtension.java @@ -18,9 +18,7 @@ ****************************************************************/ package org.apache.james.queue.rabbitmq; -import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; -import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; @@ -33,7 +31,7 @@ public class DockerRabbitMQExtension implements BeforeEachCallback, AfterEachCal @Override public void beforeEach(ExtensionContext context) { - rabbitMQ = new DockerRabbitMQ(); + rabbitMQ = DockerRabbitMQ.withoutCookie(); rabbitMQ.start(); } diff --git a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/RabbitMQClusterTest.java b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/RabbitMQClusterTest.java new file mode 100644 index 00000000000..8874641f977 --- /dev/null +++ b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/RabbitMQClusterTest.java @@ -0,0 +1,43 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ +package org.apache.james.queue.rabbitmq; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.james.queue.rabbitmq.DockerClusterRabbitMQExtention.DockerRabbitMQCluster; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(DockerClusterRabbitMQExtention.class) +class RabbitMQClusterTest { + + @Test + void rabbitMQManagerShouldReturnThreeNodesWhenAskingForStatus(DockerRabbitMQCluster cluster) throws Exception { + String stdout = cluster.getRabbitMQ1().container() + .execInContainer("rabbitmqctl", "cluster_status") + .getStdout(); + + assertThat(stdout) + .contains( + DockerClusterRabbitMQExtention.RABBIT_1, + DockerClusterRabbitMQExtention.RABBIT_2, + DockerClusterRabbitMQExtention.RABBIT_3); + } + +}