Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Kevin Wittek <kiview@users.noreply.github.com>
- Loading branch information
Showing
12 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ body: | |
- Cassandra | ||
- Clickhouse | ||
- CockroachDB | ||
- Consul | ||
- Couchbase | ||
- DB2 | ||
- Dynalite | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ body: | |
- Cassandra | ||
- Clickhouse | ||
- CockroachDB | ||
- Consul | ||
- Couchbase | ||
- DB2 | ||
- Dynalite | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ body: | |
- Cassandra | ||
- Clickhouse | ||
- CockroachDB | ||
- Consul | ||
- Couchbase | ||
- DB2 | ||
- Dynalite | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Hashicorp Consul Module | ||
|
||
Testcontainers module for [Consul](https://github.com/hashicorp/consul). Consul is a tool for managing key value properties. More information on Consul [here](https://www.consul.io/). | ||
|
||
## Usage example | ||
|
||
<!--codeinclude--> | ||
[Running Consul in your Junit tests](../../modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java) | ||
<!--/codeinclude--> | ||
|
||
## Why Consul in Junit tests? | ||
|
||
With the increasing popularity of Consul and config externalization, applications are now needing to source properties from Consul. | ||
This can prove challenging in the development phase without a running Consul instance readily on hand. This library | ||
aims to solve your apps integration testing with Consul. You can also use it to | ||
test how your application behaves with Consul by writing different test scenarios in Junit. | ||
|
||
## Adding this module to your project dependencies | ||
|
||
Add the following dependency to your `pom.xml`/`build.gradle` file: | ||
|
||
```groovy tab='Gradle' | ||
testImplementation "org.testcontainers:consul:{{latest_version}}" | ||
``` | ||
|
||
```xml tab='Maven' | ||
<dependency> | ||
<groupId>org.testcontainers</groupId> | ||
<artifactId>consul</artifactId> | ||
<version>{{latest_version}}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
``` | ||
|
||
See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/consul/AUTHORS) for contributors. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
description = "Testcontainers :: Consul" | ||
|
||
dependencies { | ||
api project(':testcontainers') | ||
|
||
testImplementation 'com.ecwid.consul:consul-api:1.4.5' | ||
testImplementation 'io.rest-assured:rest-assured:4.4.0' | ||
testImplementation 'org.assertj:assertj-core:3.21.0' | ||
} |
100 changes: 100 additions & 0 deletions
100
modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package org.testcontainers.consul; | ||
|
||
import com.github.dockerjava.api.command.InspectContainerResponse; | ||
import com.github.dockerjava.api.model.Capability; | ||
import org.testcontainers.containers.GenericContainer; | ||
import org.testcontainers.containers.wait.strategy.Wait; | ||
import org.testcontainers.utility.DockerImageName; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* Testcontainers implementation for Consul. | ||
*/ | ||
public class ConsulContainer extends GenericContainer<ConsulContainer> { | ||
|
||
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("consul"); | ||
|
||
private static final int CONSUL_HTTP_PORT = 8500; | ||
|
||
private static final int CONSUL_GRPC_PORT = 8502; | ||
|
||
private List<String> initCommands = new ArrayList<>(); | ||
|
||
private String[] startConsulCmd = new String[] { "agent", "-dev", "-client", "0.0.0.0" }; | ||
|
||
public ConsulContainer(String dockerImageName) { | ||
this(DockerImageName.parse(dockerImageName)); | ||
} | ||
|
||
public ConsulContainer(final DockerImageName dockerImageName) { | ||
super(dockerImageName); | ||
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); | ||
|
||
// Use the status leader endpoint to verify if consul is running. | ||
setWaitStrategy(Wait.forHttp("/v1/status/leader").forPort(CONSUL_HTTP_PORT).forStatusCode(200)); | ||
|
||
withCreateContainerCmdModifier(cmd -> cmd.getHostConfig().withCapAdd(Capability.IPC_LOCK)); | ||
withEnv("CONSUL_ADDR", "http://0.0.0.0:" + CONSUL_HTTP_PORT); | ||
withCommand(startConsulCmd); | ||
withExposedPorts(CONSUL_HTTP_PORT, CONSUL_GRPC_PORT); | ||
} | ||
|
||
@Override | ||
protected void containerIsStarted(InspectContainerResponse containerInfo) { | ||
runConsulCommands(); | ||
} | ||
|
||
private void runConsulCommands() { | ||
if (!initCommands.isEmpty()) { | ||
String commands = initCommands | ||
.stream() | ||
.map(command -> "consul " + command) | ||
.collect(Collectors.joining(" && ")); | ||
try { | ||
ExecResult execResult = this.execInContainer(new String[] { "/bin/sh", "-c", commands }); | ||
if (execResult.getExitCode() != 0) { | ||
logger() | ||
.error( | ||
"Failed to execute these init commands {}. Exit code {}. Stdout {}. Stderr {}", | ||
initCommands, | ||
execResult.getExitCode(), | ||
execResult.getStdout(), | ||
execResult.getStderr() | ||
); | ||
} | ||
} catch (IOException | InterruptedException e) { | ||
logger() | ||
.error( | ||
"Failed to execute these init commands {}. Exception message: {}", | ||
initCommands, | ||
e.getMessage() | ||
); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Run consul commands using the consul cli. | ||
* | ||
* Useful for enableing more secret engines like: | ||
* <pre> | ||
* .withConsulCommand("secrets enable pki") | ||
* .withConsulCommand("secrets enable transit") | ||
* </pre> | ||
* or register specific K/V like: | ||
* <pre> | ||
* .withConsulCommand("kv put config/testing1 value123") | ||
* </pre> | ||
* @param commands The commands to send to the consul cli | ||
* @return this | ||
*/ | ||
public ConsulContainer withConsulCommand(String... commands) { | ||
initCommands.addAll(Arrays.asList(commands)); | ||
return self(); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package org.testcontainers.consul; | ||
|
||
import com.ecwid.consul.v1.ConsulClient; | ||
import com.ecwid.consul.v1.Response; | ||
import com.ecwid.consul.v1.kv.model.GetValue; | ||
import io.restassured.RestAssured; | ||
import org.assertj.core.api.Assertions; | ||
import org.hamcrest.CoreMatchers; | ||
import org.hamcrest.MatcherAssert; | ||
import org.junit.ClassRule; | ||
import org.junit.Test; | ||
import org.testcontainers.containers.GenericContainer; | ||
|
||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Base64; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* This test shows the pattern to use the ConsulContainer @ClassRule for a junit test. It also has tests that ensure | ||
* the properties were added correctly by reading from Consul with the CLI and over HTTP. | ||
*/ | ||
public class ConsulContainerTest { | ||
|
||
@ClassRule | ||
public static ConsulContainer consulContainer = new ConsulContainer(ConsulTestImages.CONSUL_IMAGE) | ||
.withConsulCommand("kv put config/testing1 value123"); | ||
|
||
@Test | ||
public void readFirstPropertyPathWithCli() throws IOException, InterruptedException { | ||
GenericContainer.ExecResult result = consulContainer.execInContainer("consul", "kv", "get", "config/testing1"); | ||
final String output = result.getStdout().replaceAll("\\r?\\n", ""); | ||
MatcherAssert.assertThat(output, CoreMatchers.containsString("value123")); | ||
} | ||
|
||
@Test | ||
public void readFirstSecretPathOverHttpApi() throws InterruptedException { | ||
RestAssured | ||
.given() | ||
.when() | ||
.get("http://" + getHostAndPort() + "/v1/kv/config/testing1") | ||
.then() | ||
.assertThat() | ||
.body( | ||
"[0].Value", | ||
CoreMatchers.equalTo(Base64.getEncoder().encodeToString("value123".getBytes(StandardCharsets.UTF_8))) | ||
); | ||
} | ||
|
||
@Test | ||
public void writeAndReadMultipleValuesUsingClient() { | ||
final ConsulClient consulClient = new ConsulClient( | ||
consulContainer.getHost(), | ||
consulContainer.getFirstMappedPort() | ||
); | ||
|
||
final Map<String, String> properties = new HashMap<>(); | ||
properties.put("value", "world"); | ||
properties.put("other_value", "another world"); | ||
|
||
// Write operation | ||
properties.forEach((key, value) -> { | ||
Response<Boolean> writeResponse = consulClient.setKVValue(key, value); | ||
Assertions.assertThat(writeResponse.getValue()).isTrue(); | ||
}); | ||
|
||
// Read operation | ||
properties.forEach((key, value) -> { | ||
Response<GetValue> readResponse = consulClient.getKVValue(key); | ||
Assertions.assertThat(readResponse.getValue().getDecodedValue()).isEqualTo(value); | ||
}); | ||
} | ||
|
||
private String getHostAndPort() { | ||
return consulContainer.getHost() + ":" + consulContainer.getMappedPort(8500); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package org.testcontainers.consul; | ||
|
||
import org.testcontainers.utility.DockerImageName; | ||
|
||
public interface ConsulTestImages { | ||
DockerImageName CONSUL_IMAGE = DockerImageName.parse("consul:1.10.12"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<configuration> | ||
|
||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
<!-- encoders are assigned the type | ||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> | ||
<encoder> | ||
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<root level="INFO"> | ||
<appender-ref ref="STDOUT"/> | ||
</root> | ||
|
||
<logger name="org.testcontainers" level="DEBUG"/> | ||
</configuration> |