Skip to content

Commit e321d69

Browse files
roskhAnastasiia Smirnova
authored andcommitted
Implement Consul test container (PlaytikaOSS#691)
* implement simple consul server * implement specifying consul config file * add readme * add consul tests * cleanup code * update README Co-authored-by: Anastasiia Smirnova <[email protected]>
1 parent 187f036 commit e321d69

File tree

12 files changed

+293
-0
lines changed

12 files changed

+293
-0
lines changed

embedded-consul/README.adoc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== embedded-consul
2+
3+
==== Maven dependency
4+
5+
.pom.xml
6+
[source,xml]
7+
----
8+
<dependency>
9+
<groupId>com.playtika.testcontainers</groupId>
10+
<artifactId>embedded-consul</artifactId>
11+
<scope>test</scope>
12+
</dependency>
13+
----
14+
15+
==== Consumer (via `bootstrap.properties`)
16+
17+
* `embedded.consul.enabled` `(true|false, default is 'true')`
18+
* `embedded.consul.configurationFile` `(path for the consul configuration file to be mounted, relative to the resources folder, default is 'null')`
19+
20+
Example spring configuration:
21+
22+
.application.yml
23+
[source,yaml]
24+
----
25+
embedded:
26+
containers:
27+
enabled: true
28+
consul:
29+
enabled: true
30+
# file to be mounted in docker as '/consul/config/test-acl.hcl'
31+
# path relative from the resources directory (usually 'src/test/resources')
32+
configurationFile: consul/test-acl.hcl
33+
----
34+
35+
==== Produces
36+
37+
* `embedded.consul.host`
38+
* `embedded.consul.port`

embedded-consul/pom.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<artifactId>testcontainers-spring-boot-parent</artifactId>
8+
<groupId>com.playtika.testcontainers</groupId>
9+
<version>2.0.11-SNAPSHOT</version>
10+
<relativePath>../testcontainers-spring-boot-parent</relativePath>
11+
</parent>
12+
13+
<artifactId>embedded-consul</artifactId>
14+
15+
<properties>
16+
<consul-api-version>1.4.5</consul-api-version>
17+
</properties>
18+
19+
<dependencies>
20+
<dependency>
21+
<groupId>com.playtika.testcontainers</groupId>
22+
<artifactId>testcontainers-common</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>com.ecwid.consul</groupId>
26+
<artifactId>consul-api</artifactId>
27+
<version>${consul-api-version}</version>
28+
<scope>test</scope>
29+
</dependency>
30+
</dependencies>
31+
32+
</project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.playtika.test.consul;
2+
3+
import com.playtika.test.common.properties.CommonContainerProperties;
4+
import lombok.Data;
5+
import lombok.EqualsAndHashCode;
6+
import org.springframework.boot.context.properties.ConfigurationProperties;
7+
8+
@Data
9+
@EqualsAndHashCode(callSuper = true)
10+
@ConfigurationProperties("embedded.consul")
11+
public class ConsulProperties extends CommonContainerProperties {
12+
public static final String BEAN_NAME_EMBEDDED_CONSUL = "embeddedConsul";
13+
private String dockerImage = "consul:1.9";
14+
private int port = 8500;
15+
private String configurationFile = null;
16+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.playtika.test.consul;
2+
3+
import com.playtika.test.common.spring.DockerPresenceBootstrapConfiguration;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
6+
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
7+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
8+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.core.env.ConfigurableEnvironment;
12+
import org.springframework.core.env.MapPropertySource;
13+
import org.testcontainers.containers.BindMode;
14+
import org.testcontainers.containers.GenericContainer;
15+
import org.testcontainers.containers.wait.strategy.Wait;
16+
17+
import java.util.LinkedHashMap;
18+
19+
import static com.playtika.test.common.utils.ContainerUtils.configureCommonsAndStart;
20+
import static com.playtika.test.consul.ConsulProperties.BEAN_NAME_EMBEDDED_CONSUL;
21+
22+
@Slf4j
23+
@Configuration
24+
@ConditionalOnExpression("${embedded.containers.enabled:true}")
25+
@AutoConfigureAfter(DockerPresenceBootstrapConfiguration.class)
26+
@EnableConfigurationProperties(ConsulProperties.class)
27+
@ConditionalOnProperty(name = "embedded.consul.enabled", matchIfMissing = true)
28+
public class EmbeddedConsulBootstrapConfiguration {
29+
30+
@Bean(name = BEAN_NAME_EMBEDDED_CONSUL, destroyMethod = "stop")
31+
public GenericContainer consulContainer(ConfigurableEnvironment environment, ConsulProperties properties) {
32+
log.info("Starting consul server. Docker image {}", properties.getDockerImage());
33+
34+
GenericContainer consul = new GenericContainer<>(properties.getDockerImage())
35+
.withExposedPorts(properties.getPort())
36+
.waitingFor(
37+
Wait.forHttp("/v1/status/leader")
38+
.forStatusCode(200)
39+
).withStartupTimeout(properties.getTimeoutDuration());
40+
41+
if (properties.getConfigurationFile() != null) {
42+
consul = consul.withClasspathResourceMapping(
43+
properties.getConfigurationFile(), "/consul/config/test.hcl",
44+
BindMode.READ_ONLY);
45+
}
46+
47+
consul = configureCommonsAndStart(consul, properties, log);
48+
registerConsulEnvironment(consul, environment, properties);
49+
return consul;
50+
}
51+
52+
private void registerConsulEnvironment(GenericContainer consul, ConfigurableEnvironment environment,
53+
ConsulProperties properties) {
54+
Integer mappedPort = consul.getMappedPort(properties.getPort());
55+
String host = consul.getContainerIpAddress();
56+
57+
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
58+
map.put("embedded.consul.port", mappedPort);
59+
map.put("embedded.consul.host", host);
60+
61+
log.info("Started consul. Connection Details: {}", map);
62+
63+
MapPropertySource propertySource = new MapPropertySource("embeddedConsulInfo", map);
64+
environment.getPropertySources().addFirst(propertySource);
65+
}
66+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
2+
com.playtika.test.consul.EmbeddedConsulBootstrapConfiguration
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.playtika.test.consul;
2+
3+
import com.ecwid.consul.v1.ConsulClient;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.core.env.ConfigurableEnvironment;
6+
import org.testcontainers.containers.GenericContainer;
7+
8+
public class EmbeddedConsulBootstrapConfigurationBaseTest {
9+
@Autowired
10+
protected ConfigurableEnvironment environment;
11+
12+
@Autowired
13+
protected GenericContainer consulContainer;
14+
15+
protected ConsulClient buildClient() {
16+
return new ConsulClient(consulContainer.getHost(), consulContainer.getFirstMappedPort());
17+
}
18+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.playtika.test.consul;
2+
3+
import com.ecwid.consul.v1.ConsulClient;
4+
import com.ecwid.consul.v1.OperationException;
5+
import org.junit.jupiter.api.Test;
6+
import org.springframework.boot.test.context.SpringBootTest;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
10+
11+
@SpringBootTest(
12+
properties = {
13+
"embedded.consul.enabled=true",
14+
"embedded.consul.configurationFile=consul.hcl"
15+
},
16+
classes = TestConfiguration.class
17+
)
18+
public class EmbeddedConsulBootstrapConfigurationConfigTest extends EmbeddedConsulBootstrapConfigurationBaseTest {
19+
20+
@Test
21+
public void propertiesAvailable() {
22+
assertThat(environment.getProperty("embedded.consul.enabled"))
23+
.isEqualTo("true");
24+
assertThat(environment.getProperty("embedded.consul.host"))
25+
.isEqualTo(consulContainer.getHost());
26+
assertThat(environment.getProperty("embedded.consul.port"))
27+
.isEqualTo(consulContainer.getFirstMappedPort().toString());
28+
assertThat(environment.getProperty("embedded.consul.configurationFile"))
29+
.isEqualTo("consul.hcl");
30+
}
31+
32+
@Test
33+
public void shouldUpdateKeyForbidden() {
34+
ConsulClient client = buildClient();
35+
36+
// with the loaded config consul should require use of an access token,
37+
// which is not provided so 403 should be returned by consul
38+
OperationException ex = assertThrows(OperationException.class, () -> {
39+
client.setKVValue("key", "val");
40+
});
41+
assertThat(ex.getStatusCode()).isEqualTo(403);
42+
}
43+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.playtika.test.consul;
2+
3+
import com.ecwid.consul.v1.ConsulClient;
4+
import com.ecwid.consul.v1.Response;
5+
import org.junit.jupiter.api.Test;
6+
import org.springframework.boot.test.context.SpringBootTest;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
@SpringBootTest(
11+
properties = "embedded.consul.enabled=true",
12+
classes = TestConfiguration.class
13+
)
14+
public class EmbeddedConsulBootstrapConfigurationTest extends EmbeddedConsulBootstrapConfigurationBaseTest {
15+
16+
@Test
17+
public void propertiesAvailable() {
18+
assertThat(environment.getProperty("embedded.consul.enabled"))
19+
.isEqualTo("true");
20+
assertThat(environment.getProperty("embedded.consul.configurationFile"))
21+
.isEqualTo(null);
22+
assertThat(environment.getProperty("embedded.consul.host"))
23+
.isEqualTo(consulContainer.getHost());
24+
assertThat(environment.getProperty("embedded.consul.port"))
25+
.isEqualTo(consulContainer.getFirstMappedPort().toString());
26+
}
27+
28+
@Test
29+
public void shouldUpdateKey() {
30+
ConsulClient client = buildClient();
31+
32+
Response<Boolean> booleanResponse = client.setKVValue("key", "val");
33+
assertThat(booleanResponse.getValue()).isEqualTo(true);
34+
}
35+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.playtika.test.consul;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.beans.factory.BeanFactoryUtils;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
7+
import org.springframework.boot.test.context.SpringBootTest;
8+
import org.testcontainers.containers.GenericContainer;
9+
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
12+
@SpringBootTest(
13+
properties = "embedded.consul.enabled=false",
14+
classes = TestConfiguration.class
15+
)
16+
public class EmbeddedConsulDisabledTest {
17+
@Autowired
18+
ConfigurableListableBeanFactory beanFactory;
19+
20+
@Test
21+
public void contextLoads() {
22+
String[] containers = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, GenericContainer.class);
23+
assertThat(containers).isEmpty();
24+
}
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.playtika.test.consul;
2+
3+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
4+
import org.springframework.context.annotation.Configuration;
5+
6+
@EnableAutoConfiguration
7+
@Configuration
8+
class TestConfiguration {
9+
}

0 commit comments

Comments
 (0)