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

Add RabbitMQ module #1449

Merged
merged 26 commits into from Jul 16, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b405963
Added RabbitMQ module
martingreber May 1, 2019
df8a6ce
Added SSL option for RabbitMQ
martingreber May 2, 2019
3c60e78
Update modules/rabbitmq/src/main/java/org/testcontainers/containers/R…
bsideup May 3, 2019
450d347
Update modules/rabbitmq/src/main/java/org/testcontainers/containers/R…
bsideup May 3, 2019
6bf7441
Fixed bug in enabling RabbitMQ plugin
martingreber May 3, 2019
75b6c47
Changed builder methods to return RabbitMQContainer
martingreber May 3, 2019
f761f0b
Replaced org.json with jackson
martingreber May 4, 2019
175f65c
Removed option to disable plugins. It shouldn't be needed
martingreber May 4, 2019
c8bcf85
Removed unused code
martingreber May 4, 2019
23fc6d6
Simplified wait condition
martingreber May 4, 2019
c7baa8f
Removed unused exception class
martingreber May 4, 2019
b5703d4
Use try with resources for tests
martingreber May 4, 2019
94fcfe5
Fixed copy-paste comment bug from the Neo4JContainer. Cheers to the N…
martingreber May 7, 2019
5acfdf0
Removed getLivenessCheckPortNumbers() as the super implementation is …
martingreber May 7, 2019
fa07280
Removed unnecessary wait from test
martingreber May 7, 2019
c194123
Refactored methods returning URLs and json string generation
martingreber May 7, 2019
831d76b
Fixed bug with missing verify_peer option
martingreber May 7, 2019
4701201
Restructured code for readability
martingreber May 7, 2019
2a51f88
Merge branch 'master' into add-rabbitmq
martingreber May 7, 2019
a95f172
Add Lightbend to the Who is using list (#1464)
ennru May 14, 2019
3643f98
Merge branch 'master' into add-rabbitmq
martingreber May 15, 2019
79c0413
Merge branch 'master' into add-rabbitmq
rnorth May 18, 2019
d158a7c
Merge branch 'master' into add-rabbitmq
martingreber May 23, 2019
2e04112
Merge branch 'master' into add-rabbitmq
May 27, 2019
72e1ebd
accept image, not tag
bsideup Jul 16, 2019
1d637fc
Update RabbitMQContainerTest.java
bsideup Jul 16, 2019
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
7 changes: 7 additions & 0 deletions modules/rabbitmq/build.gradle
@@ -0,0 +1,7 @@
description = "TestContainers :: RabbitMQ"

dependencies {
compile project(":testcontainers")
testCompile "com.rabbitmq:amqp-client:5.7.0"
testCompile 'org.assertj:assertj-core:3.12.2'
}
@@ -0,0 +1,367 @@
package org.testcontainers.containers;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.command.InspectContainerResponse;
import org.jetbrains.annotations.NotNull;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.MountableFile;

import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static java.lang.String.join;
import static java.util.Arrays.asList;

/**
* Testcontainer for RabbitMQ.
*
* @author Martin Greber
*/
public class RabbitMQContainer extends GenericContainer<RabbitMQContainer> {

/**
* The image defaults to the official RabbitmQ image: <a href="https://hub.docker.com/_/rabbitmq/">RabbitMQ</a>.
*/
private static final String DEFAULT_IMAGE_NAME = "rabbitmq";
private static final String DEFAULT_TAG = "3.7-management-alpine";

private static final int DEFAULT_AMQP_PORT = 5672;
private static final int DEFAULT_AMQPS_PORT = 5671;
private static final int DEFAULT_HTTPS_PORT = 15671;
private static final int DEFAULT_HTTP_PORT = 15672;

private String adminPassword = "guest";
private String adminUsername = "guest";
private final List<List<String>> values = new ArrayList<>();

/**
* Creates a Testcontainer using the official RabbitMQ docker image.
*/
public RabbitMQContainer() {
this(DEFAULT_TAG);
}

/**
* Creates a Testcontainer using a specific docker image.
*
* @param tag The docker image to use.
*/
public RabbitMQContainer(String tag) {
super(DEFAULT_IMAGE_NAME + ":" + tag);

addExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_AMQPS_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT);

this.waitStrategy = Wait.
forLogMessage(".*Server startup complete.*", 1)
.withStartupTimeout(Duration.ofSeconds(60));
}

@Override
protected void configure() {
if (adminPassword != null) {
addEnv("RABBITMQ_DEFAULT_PASS", adminPassword);
}
}

@Override
protected void containerIsStarted(InspectContainerResponse containerInfo) {

values.forEach(command -> {
try {
ExecResult execResult = execInContainer(command.toArray(new String[0]));
if (execResult.getExitCode() != 0) {
logger().error("Could not execute command {}: {}", command, execResult.getStderr());
}
} catch (IOException | InterruptedException e) {
logger().error("Could not execute command {}: {}", command, e.getMessage());
}
});
}

/**
* @return The admin password for the <code>admin</code> account
*/
public String getAdminPassword() {
return adminPassword;
}

public String getAdminUsername() {
return adminUsername;
}

public Integer getAmqpPort() {
return getMappedPort(DEFAULT_AMQP_PORT);
}

public Integer getAmqpsPort() {
return getMappedPort(DEFAULT_AMQPS_PORT);
}

public Integer getHttpsPort() {
return getMappedPort(DEFAULT_HTTPS_PORT);
}

public Integer getHttpPort() {
return getMappedPort(DEFAULT_HTTP_PORT);
}

/**
* @return AMQP URL for use with AMQP clients.
*/
public String getAmqpUrl() {
return "amqp://" + getContainerIpAddress() + ":" + getAmqpPort();
}

/**
* @return AMQPS URL for use with AMQPS clients.
*/
public String getAmqpsUrl() {
return "amqps://" + getContainerIpAddress() + ":" + getAmqpsPort();
}

/**
* @return URL of the HTTP management endpoint.
*/
public String getHttpUrl() {
return "http://" + getContainerIpAddress() + ":" + getHttpPort();
}

/**
* @return URL of the HTTPS management endpoint.
*/
public String getHttpsUrl() {
return "https://" + getContainerIpAddress() + ":" + getHttpsPort();
}

/**
* Sets the password for the admin (default is <pre>guest</pre>)
*
* @param adminPassword The admin password.
* @return This container.
*/
public RabbitMQContainer withAdminPassword(final String adminPassword) {
this.adminPassword = adminPassword;
return this;
}

public enum SslVerification {
VERIFY_NONE("verify_none"), VERIFY_PEER("verify_peer");

SslVerification(String value) {
this.value = value;
}
private final String value;

}

public RabbitMQContainer withSSL(
final MountableFile keyFile,
final MountableFile certFile,
final MountableFile caFile,
final SslVerification verify,
boolean failIfNoCert,
int verificationDepth) {

return withSSL(keyFile, certFile, caFile, verify, failIfNoCert)
.withEnv("RABBITMQ_SSL_DEPTH", String.valueOf(verificationDepth));
}

public RabbitMQContainer withSSL(
final MountableFile keyFile,
final MountableFile certFile,
final MountableFile caFile,
final SslVerification verify,
boolean failIfNoCert) {

return withSSL(keyFile, certFile, caFile, verify)
.withEnv("RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT", String.valueOf(failIfNoCert));
}

public RabbitMQContainer withSSL(
final MountableFile keyFile,
final MountableFile certFile,
final MountableFile caFile,
final SslVerification verify) {

return withEnv("RABBITMQ_SSL_CACERTFILE", "/etc/rabbitmq/ca_cert.pem")
.withEnv("RABBITMQ_SSL_CERTFILE", "/etc/rabbitmq/rabbitmq_cert.pem")
.withEnv("RABBITMQ_SSL_KEYFILE", "/etc/rabbitmq/rabbitmq_key.pem")
.withEnv("RABBITMQ_SSL_VERIFY", verify.value)
.withCopyFileToContainer(certFile, "/etc/rabbitmq/rabbitmq_cert.pem")
.withCopyFileToContainer(caFile, "/etc/rabbitmq/ca_cert.pem")
.withCopyFileToContainer(keyFile, "/etc/rabbitmq/rabbitmq_key.pem");
}

public RabbitMQContainer withPluginsEnabled(String... pluginNames) {
List<String> command = new ArrayList<>(asList("rabbitmq-plugins", "enable"));
command.addAll(asList(pluginNames));
values.add(command);
bsideup marked this conversation as resolved.
Show resolved Hide resolved
return self();
}

public RabbitMQContainer withBinding(String source, String destination) {
values.add(asList("rabbitmqadmin", "declare", "binding",
"source=" + source,
"destination=" + destination));
return self();
}

public RabbitMQContainer withBinding(String source, String destination, Map<String, Object> arguments, String routingKey, String destinationType) {
values.add(asList("rabbitmqadmin", "declare", "binding",
"source=" + source,
"destination=" + destination,
"routing-key=" + routingKey,
"destination-type=" + destinationType,
"arguments=" + toJson(arguments)));
return self();
}

public RabbitMQContainer withParameter(String component, String name, String value) {
values.add(asList("rabbitmqadmin", "declare", "parameter",
"component=" + component,
"name=" + name,
"value=" + value));
return self();
}

public RabbitMQContainer withPermission(String vhost, String user, String configure, String write, String read) {
values.add(asList("rabbitmqadmin", "declare", "permission",
"vhost=" + vhost,
"user=" + user,
"configure=" + configure,
"write=" + write,
"read=" + read));
return self();
}

public RabbitMQContainer withUser(String name, String password) {
values.add(asList("rabbitmqadmin", "declare", "user",
"name=" + name,
"password=" + password,
"tags="));
return self();
}

public RabbitMQContainer withUser(String name, String password, Set<String> tags) {
values.add(asList("rabbitmqadmin", "declare", "user",
"name=" + name,
"password=" + password,
"tags=" + join(",", tags)));
return self();
}

public RabbitMQContainer withPolicy(String name, String pattern, Map<String, Object> definition) {
values.add(asList("rabbitmqadmin", "declare", "policy",
"name=" + name,
"pattern=" + pattern,
"definition=" + toJson(definition)));
return self();
}

public RabbitMQContainer withPolicy(String name, String pattern, Map<String, Object> definition, int priority, String applyTo) {
values.add(asList("rabbitmqadmin", "declare", "policy",
"name=" + name,
"pattern=" + pattern,
"priority=" + priority,
"apply-to=" + applyTo,
"definition=" + toJson(definition)));
return self();
}

public RabbitMQContainer withOperatorPolicy(String name, String pattern, Map<String, Object> definition) {
values.add(new ArrayList<>(asList("rabbitmqadmin", "declare", "operator_policy",
"name=" + name,
"pattern=" + pattern,
"definition=" + toJson(definition))));
return self();
}

public RabbitMQContainer withOperatorPolicy(String name, String pattern, Map<String, Object> definition, int priority, String applyTo) {
values.add(asList("rabbitmqadmin", "declare", "operator_policy",
"name=" + name,
"pattern=" + pattern,
"priority=" + priority,
"apply-to=" + applyTo,
"definition=" + toJson(definition)));
return self();
}

public RabbitMQContainer withVhost(String name) {
values.add(asList("rabbitmqadmin", "declare", "vhost",
"name=" + name));
return self();
}

public RabbitMQContainer withVhost(String name, boolean tracing) {
values.add(asList("rabbitmqadmin", "declare", "vhost",
"name=" + name,
"tracing=" + tracing));
return self();
}

public RabbitMQContainer withVhostLimit(String vhost, String name, int value) {
values.add(asList("rabbitmqadmin", "declare", "vhost_limit",
"vhost=" + vhost,
"name=" + name,
"value=" + value));
return self();
}

public RabbitMQContainer withQueue(String name) {
values.add(asList("rabbitmqadmin", "declare", "queue",
"name=" + name));
return self();
}

public RabbitMQContainer withQueue(String name, boolean autoDelete, boolean durable, Map<String, Object> arguments) {
values.add(asList("rabbitmqadmin", "declare", "queue",
"name=" + name,
"auto_delete=" + autoDelete,
"durable=" + durable,
"arguments=" + toJson(arguments)));
return self();
}

public RabbitMQContainer withExchange(String name, String type) {
values.add(asList("rabbitmqadmin", "declare", "exchange",
"name=" + name,
"type=" + type));
return self();
}

public RabbitMQContainer withExchange(String name, String type, boolean autoDelete, boolean internal, boolean durable, Map<String, Object> arguments) {
values.add(asList("rabbitmqadmin", "declare", "exchange",
"name=" + name,
"type=" + type,
"auto_delete=" + autoDelete,
"internal=" + internal,
"durable=" + durable,
"arguments=" + toJson(arguments)));
return self();
}

/**
* Overwrites the default RabbitMQ configuration file with the supplied one
*
* @param rabbitMQConf The rabbitmq.conf file to use
* @return This container.
*/
public RabbitMQContainer withRabbitMQConfig(MountableFile rabbitMQConf) {
withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom.conf");
return withCopyFileToContainer(rabbitMQConf, "/etc/rabbitmq/rabbitmq-custom.conf");
}

@NotNull
private String toJson(Map<String, Object> arguments) {
try {
return new ObjectMapper().writeValueAsString(arguments);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to convert arguments into json: " + e.getMessage(), e);
}
}
}
@@ -0,0 +1,22 @@
package org.testcontainers.containers;

import org.junit.ClassRule;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Test for basic functionality when used as a <code>@ClassRule</code>.
*
* @author Mrtin Greber
*/
public class RabbitMQContainerJUnitIntegrationTest {

@ClassRule
public static RabbitMQContainer rabbitMQContainer = new RabbitMQContainer();

@Test
public void shouldStart() {
assertThat(rabbitMQContainer.isRunning()).isTrue();
}
}