Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions embedded-consul/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
=== embedded-consul

==== Maven dependency

.pom.xml
[source,xml]
----
<dependency>
<groupId>com.playtika.testcontainers</groupId>
<artifactId>embedded-consul</artifactId>
<scope>test</scope>
</dependency>
----

==== Consumer (via `bootstrap.properties`)

* `embedded.consul.enabled` `(true|false, default is 'true')`
* `embedded.consul.configurationFile` `(path for the consul configuration file to be mounted, relative to the resources folder, default is 'null')`

Example spring configuration:

.application.yml
[source,yaml]
----
embedded:
containers:
enabled: true
consul:
enabled: true
# file to be mounted in docker as '/consul/config/test-acl.hcl'
# path relative from the resources directory (usually 'src/test/resources')
configurationFile: consul/test-acl.hcl
----

==== Produces

* `embedded.consul.host`
* `embedded.consul.port`
32 changes: 32 additions & 0 deletions embedded-consul/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>testcontainers-spring-boot-parent</artifactId>
<groupId>com.playtika.testcontainers</groupId>
<version>2.0.11-SNAPSHOT</version>
<relativePath>../testcontainers-spring-boot-parent</relativePath>
</parent>

<artifactId>embedded-consul</artifactId>

<properties>
<consul-api-version>1.4.5</consul-api-version>
</properties>

<dependencies>
<dependency>
<groupId>com.playtika.testcontainers</groupId>
<artifactId>testcontainers-common</artifactId>
</dependency>
<dependency>
<groupId>com.ecwid.consul</groupId>
<artifactId>consul-api</artifactId>
<version>${consul-api-version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.playtika.test.consul;

import com.playtika.test.common.properties.CommonContainerProperties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties("embedded.consul")
public class ConsulProperties extends CommonContainerProperties {
public static final String BEAN_NAME_EMBEDDED_CONSUL = "embeddedConsul";
private String dockerImage = "consul:1.9";
private int port = 8500;
private String configurationFile = null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.playtika.test.consul;

import com.playtika.test.common.spring.DockerPresenceBootstrapConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;

import java.util.LinkedHashMap;

import static com.playtika.test.common.utils.ContainerUtils.configureCommonsAndStart;
import static com.playtika.test.consul.ConsulProperties.BEAN_NAME_EMBEDDED_CONSUL;

@Slf4j
@Configuration
@ConditionalOnExpression("${embedded.containers.enabled:true}")
@AutoConfigureAfter(DockerPresenceBootstrapConfiguration.class)
@EnableConfigurationProperties(ConsulProperties.class)
@ConditionalOnProperty(name = "embedded.consul.enabled", matchIfMissing = true)
public class EmbeddedConsulBootstrapConfiguration {

@Bean(name = BEAN_NAME_EMBEDDED_CONSUL, destroyMethod = "stop")
public GenericContainer consulContainer(ConfigurableEnvironment environment, ConsulProperties properties) {
log.info("Starting consul server. Docker image {}", properties.getDockerImage());

GenericContainer consul = new GenericContainer<>(properties.getDockerImage())
.withExposedPorts(properties.getPort())
.waitingFor(
Wait.forHttp("/v1/status/leader")
.forStatusCode(200)
).withStartupTimeout(properties.getTimeoutDuration());

if (properties.getConfigurationFile() != null) {
consul = consul.withClasspathResourceMapping(
properties.getConfigurationFile(), "/consul/config/test.hcl",
BindMode.READ_ONLY);
}

consul = configureCommonsAndStart(consul, properties, log);
registerConsulEnvironment(consul, environment, properties);
return consul;
}

private void registerConsulEnvironment(GenericContainer consul, ConfigurableEnvironment environment,
ConsulProperties properties) {
Integer mappedPort = consul.getMappedPort(properties.getPort());
String host = consul.getContainerIpAddress();

LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("embedded.consul.port", mappedPort);
map.put("embedded.consul.host", host);

log.info("Started consul. Connection Details: {}", map);

MapPropertySource propertySource = new MapPropertySource("embeddedConsulInfo", map);
environment.getPropertySources().addFirst(propertySource);
}
}
2 changes: 2 additions & 0 deletions embedded-consul/src/main/resources/META-INF/spring.factories
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.playtika.test.consul.EmbeddedConsulBootstrapConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.playtika.test.consul;

import com.ecwid.consul.v1.ConsulClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.ConfigurableEnvironment;
import org.testcontainers.containers.GenericContainer;

public class EmbeddedConsulBootstrapConfigurationBaseTest {
@Autowired
protected ConfigurableEnvironment environment;

@Autowired
protected GenericContainer consulContainer;

protected ConsulClient buildClient() {
return new ConsulClient(consulContainer.getHost(), consulContainer.getFirstMappedPort());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.playtika.test.consul;

import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.OperationException;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

@SpringBootTest(
properties = {
"embedded.consul.enabled=true",
"embedded.consul.configurationFile=consul.hcl"
},
classes = TestConfiguration.class
)
public class EmbeddedConsulBootstrapConfigurationConfigTest extends EmbeddedConsulBootstrapConfigurationBaseTest {

@Test
public void propertiesAvailable() {
assertThat(environment.getProperty("embedded.consul.enabled"))
.isEqualTo("true");
assertThat(environment.getProperty("embedded.consul.host"))
.isEqualTo(consulContainer.getHost());
assertThat(environment.getProperty("embedded.consul.port"))
.isEqualTo(consulContainer.getFirstMappedPort().toString());
assertThat(environment.getProperty("embedded.consul.configurationFile"))
.isEqualTo("consul.hcl");
}

@Test
public void shouldUpdateKeyForbidden() {
ConsulClient client = buildClient();

// with the loaded config consul should require use of an access token,
// which is not provided so 403 should be returned by consul
OperationException ex = assertThrows(OperationException.class, () -> {
client.setKVValue("key", "val");
});
assertThat(ex.getStatusCode()).isEqualTo(403);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.playtika.test.consul;

import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.Response;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

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

@SpringBootTest(
properties = "embedded.consul.enabled=true",
classes = TestConfiguration.class
)
public class EmbeddedConsulBootstrapConfigurationTest extends EmbeddedConsulBootstrapConfigurationBaseTest {

@Test
public void propertiesAvailable() {
assertThat(environment.getProperty("embedded.consul.enabled"))
.isEqualTo("true");
assertThat(environment.getProperty("embedded.consul.configurationFile"))
.isEqualTo(null);
assertThat(environment.getProperty("embedded.consul.host"))
.isEqualTo(consulContainer.getHost());
assertThat(environment.getProperty("embedded.consul.port"))
.isEqualTo(consulContainer.getFirstMappedPort().toString());
}

@Test
public void shouldUpdateKey() {
ConsulClient client = buildClient();

Response<Boolean> booleanResponse = client.setKVValue("key", "val");
assertThat(booleanResponse.getValue()).isEqualTo(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.playtika.test.consul;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.testcontainers.containers.GenericContainer;

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

@SpringBootTest(
properties = "embedded.consul.enabled=false",
classes = TestConfiguration.class
)
public class EmbeddedConsulDisabledTest {
@Autowired
ConfigurableListableBeanFactory beanFactory;

@Test
public void contextLoads() {
String[] containers = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, GenericContainer.class);
assertThat(containers).isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.playtika.test.consul;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;

@EnableAutoConfiguration
@Configuration
class TestConfiguration {
}
8 changes: 8 additions & 0 deletions embedded-consul/src/test/resources/consul.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
acl {
enabled = true
default_policy = "deny"
down_policy = "extend-cache"
tokens = {
master = "78b7ad52-1f0b-e100-7b02-000001122333" # master consul access_token, use as bearer token in HTTP requests
}
}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<module>embedded-vertica</module>
<module>embedded-prometheus</module>
<module>embedded-grafana</module>
<module>embedded-consul</module>
</modules>

<properties>
Expand Down