Skip to content

Commit 90d0fe0

Browse files
Add embedded zookeeper (PlaytikaOSS#1347)
Co-authored-by: Igor Yova <[email protected]>
1 parent ccfb589 commit 90d0fe0

File tree

11 files changed

+347
-0
lines changed

11 files changed

+347
-0
lines changed

embedded-zookeeper/README.adoc

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
=== embedded-zookeeper
2+
3+
==== Maven dependency
4+
5+
.pom.xml
6+
[source,xml]
7+
----
8+
<dependency>
9+
<groupId>com.playtika.testcontainers</groupId>
10+
<artifactId>embedded-zookeeper</artifactId>
11+
<scope>test</scope>
12+
</dependency>
13+
----
14+
15+
==== Consumes (via `bootstrap.properties`)
16+
17+
* `embedded.zookeeper.enabled` `(true|false, default is true)`
18+
19+
==== Produces
20+
21+
* `embedded.zookeeper.host`
22+
* `embedded.zookeeper.port`
23+
* `embedded.zookeeper.admin.port`
24+
25+
==== Example (Spring Boot)
26+
27+
===== Example spring configuration:
28+
29+
application-test.yml
30+
[source,yaml]
31+
----
32+
embedded:
33+
containers:
34+
enabled: true
35+
zookeeper:
36+
enabled: true
37+
----
38+
39+
===== Create vanilla client
40+
Create vanilla client pointed directly to Zookeeper server using properties provided by `embedded-zookeeper`:
41+
42+
[source,java]
43+
----
44+
private static final int DEFAULT_SESSION_TIMEOUT_MS = 60000;
45+
46+
@Bean(destroyMethod = "close")
47+
public ZooKeeper zookeeperClient(@Value("${embedded.zookeeper.host}") String host,
48+
@Value("${embedded.zookeeper.port}") int port) throws Exception {
49+
CountDownLatch connSignal = new CountDownLatch(1);
50+
String connectionString = host + ":" + port;
51+
ZooKeeper zooKeeper = new ZooKeeper(connectionString, DEFAULT_SESSION_TIMEOUT_MS, event -> {
52+
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
53+
connSignal.countDown();
54+
}
55+
});
56+
connSignal.await();
57+
return zooKeeper;
58+
}
59+
----

embedded-zookeeper/pom.xml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
<parent>
6+
<artifactId>testcontainers-spring-boot-parent</artifactId>
7+
<groupId>com.playtika.testcontainers</groupId>
8+
<version>3.0.0-RC7</version>
9+
<relativePath>../testcontainers-spring-boot-parent</relativePath>
10+
</parent>
11+
<modelVersion>4.0.0</modelVersion>
12+
13+
<artifactId>embedded-zookeeper</artifactId>
14+
15+
<properties>
16+
<zookeeper-client.version>3.8.1</zookeeper-client.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.playtika.testcontainers</groupId>
26+
<artifactId>embedded-toxiproxy</artifactId>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.apache.zookeeper</groupId>
30+
<artifactId>zookeeper</artifactId>
31+
<version>${zookeeper-client.version}</version>
32+
<scope>test</scope>
33+
<exclusions>
34+
<exclusion>
35+
<groupId>ch.qos.logback</groupId>
36+
<artifactId>logback-core</artifactId>
37+
</exclusion>
38+
<exclusion>
39+
<groupId>ch.qos.logback</groupId>
40+
<artifactId>logback-classic</artifactId>
41+
</exclusion>
42+
</exclusions>
43+
</dependency>
44+
</dependencies>
45+
46+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.playtika.testcontainers.zookeeper;
2+
3+
import com.playtika.testcontainer.common.spring.DockerPresenceBootstrapConfiguration;
4+
import com.playtika.testcontainer.common.utils.ContainerUtils;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
7+
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
8+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
9+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.core.env.ConfigurableEnvironment;
13+
import org.springframework.core.env.MapPropertySource;
14+
import org.testcontainers.containers.GenericContainer;
15+
import org.testcontainers.containers.Network;
16+
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
17+
import org.testcontainers.containers.wait.strategy.Wait;
18+
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
19+
import org.testcontainers.containers.wait.strategy.WaitStrategy;
20+
21+
import java.util.LinkedHashMap;
22+
import java.util.Optional;
23+
24+
import static com.playtika.testcontainer.common.utils.ContainerUtils.configureCommonsAndStart;
25+
26+
@Slf4j
27+
@Configuration
28+
@ConditionalOnExpression("${embedded.containers.enabled:true}")
29+
@AutoConfigureAfter(DockerPresenceBootstrapConfiguration.class)
30+
@ConditionalOnProperty(name = "embedded.zookeeper.enabled", havingValue = "true", matchIfMissing = true)
31+
@EnableConfigurationProperties(ZookeeperConfigurationProperties.class)
32+
public class EmbeddedZookeeperBootstrapConfiguration {
33+
34+
@Bean(destroyMethod = "stop")
35+
public GenericContainer<?> zooKeeperContainer(ConfigurableEnvironment environment,
36+
ZookeeperConfigurationProperties properties,
37+
Optional<Network> network) {
38+
WaitStrategy waitStrategy = new WaitAllStrategy()
39+
.withStrategy(new HostPortWaitStrategy())
40+
.withStrategy(Wait.forHttp("/commands/ruok")
41+
.forPort(properties.adminServerPort)
42+
.forStatusCode(200)
43+
)
44+
.withStartupTimeout(properties.getTimeoutDuration());
45+
GenericContainer<?> zookeeper = new GenericContainer<>(ContainerUtils.getDockerImageName(properties))
46+
.withExposedPorts(properties.clientPort, properties.adminServerPort)
47+
.withEnv("ZOO_ADMINSERVER_ENABLED", String.valueOf(true))
48+
.waitingFor(waitStrategy);
49+
network.ifPresent(zookeeper::withNetwork);
50+
51+
zookeeper = configureCommonsAndStart(zookeeper, properties, log);
52+
registerZookeeperEnvironment(zookeeper, environment, properties);
53+
54+
return zookeeper;
55+
}
56+
57+
private void registerZookeeperEnvironment(GenericContainer<?> zookeeper, ConfigurableEnvironment environment,
58+
ZookeeperConfigurationProperties properties) {
59+
String host = zookeeper.getHost();
60+
61+
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
62+
map.put("embedded.zookeeper.port", zookeeper.getMappedPort(properties.clientPort));
63+
map.put("embedded.zookeeper.admin.port", zookeeper.getMappedPort(properties.adminServerPort));
64+
map.put("embedded.zookeeper.host", host);
65+
66+
log.info("Started Zookeeper. Connection Details: {}", map);
67+
68+
MapPropertySource propertySource = new MapPropertySource("embeddedZookeeperInfo", map);
69+
environment.getPropertySources().addFirst(propertySource);
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.playtika.testcontainers.zookeeper;
2+
3+
import com.playtika.testcontainer.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.zookeeper")
11+
public class ZookeeperConfigurationProperties extends CommonContainerProperties {
12+
13+
protected int clientPort = 2181;
14+
protected int adminServerPort = 8080;
15+
16+
protected String host = "localhost";
17+
18+
// https://hub.docker.com/_/zookeeper
19+
@Override
20+
public String getDefaultDockerImage() {
21+
// Please don`t remove this comment.
22+
// renovate: datasource=docker
23+
return "zookeeper:3.8.1";
24+
}
25+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"groups": [
3+
],
4+
"properties": [
5+
{
6+
"name": "embedded.zookeeper.enabled",
7+
"type": "java.lang.Boolean",
8+
"defaultValue": "true"
9+
}
10+
],
11+
"hints": [
12+
{
13+
"name": "embedded.zookeeper.enabled",
14+
"values": [
15+
{
16+
"value": "true",
17+
"description": "Enables configuration of Zookeeper server on startup."
18+
},
19+
{
20+
"value": "false",
21+
"description": "Disabled configuration of Zookeeper server on startup."
22+
}
23+
]
24+
}
25+
]
26+
}
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.testcontainers.zookeeper.EmbeddedZookeeperBootstrapConfiguration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.playtika.testcontainers.zookeeper;
2+
3+
import org.apache.zookeeper.Watcher;
4+
import org.apache.zookeeper.ZooKeeper;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.core.env.ConfigurableEnvironment;
11+
import org.testcontainers.containers.GenericContainer;
12+
13+
import java.util.concurrent.CountDownLatch;
14+
15+
public abstract class EmbeddedZookeeperBootstrapConfigurationBaseTest {
16+
@Autowired
17+
protected ConfigurableEnvironment environment;
18+
19+
@Autowired
20+
protected GenericContainer<?> zooKeeperContainer;
21+
22+
@EnableAutoConfiguration
23+
@Configuration
24+
static class TestConfiguration {
25+
private static final int DEFAULT_SESSION_TIMEOUT_MS = 60_000;
26+
27+
@Bean(destroyMethod = "close")
28+
public ZooKeeper zookeeperClient(@Value("${embedded.zookeeper.host}") String host,
29+
@Value("${embedded.zookeeper.port}") int port) throws Exception {
30+
CountDownLatch connSignal = new CountDownLatch(1);
31+
String connectionString = host + ":" + port;
32+
ZooKeeper zooKeeper = new ZooKeeper(connectionString, DEFAULT_SESSION_TIMEOUT_MS, event -> {
33+
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
34+
connSignal.countDown();
35+
}
36+
});
37+
connSignal.await();
38+
return zooKeeper;
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.playtika.testcontainers.zookeeper;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.apache.zookeeper.ZooKeeper;
5+
import org.junit.jupiter.api.Test;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.boot.test.context.SpringBootTest;
8+
9+
import java.util.List;
10+
11+
import static org.assertj.core.api.Assertions.assertThat;
12+
13+
@Slf4j
14+
@SpringBootTest(
15+
classes = EmbeddedZookeeperBootstrapConfigurationTest.TestConfiguration.class,
16+
properties = {
17+
"embedded.zookeeper.enabled=true"
18+
}
19+
)
20+
public class EmbeddedZookeeperBootstrapConfigurationTest extends EmbeddedZookeeperBootstrapConfigurationBaseTest {
21+
@Autowired
22+
private ZooKeeper zookeeper;
23+
@Test
24+
void propertiesAreAvailable() {
25+
assertThat(environment.getProperty("embedded.zookeeper.host")).isNotEmpty();
26+
assertThat(environment.getProperty("embedded.zookeeper.admin.port")).isNotEmpty();
27+
assertThat(environment.getProperty("embedded.zookeeper.port")).isNotEmpty();
28+
}
29+
30+
@Test
31+
void shouldConnectToRoot() throws Exception {
32+
List<String> list = zookeeper.getChildren("/", false);
33+
assertThat(list).isNotEmpty();
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.playtika.testcontainers.zookeeper;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.boot.autoconfigure.AutoConfigurations;
5+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
6+
import org.testcontainers.containers.Container;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
public class EmbeddedZookeeperDisabledTest {
11+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
12+
.withConfiguration(AutoConfigurations.of(EmbeddedZookeeperBootstrapConfiguration.class));
13+
14+
@Test
15+
void contextLoads() {
16+
contextRunner
17+
.withPropertyValues("embedded.zookeeper.enabled=false")
18+
.run(context -> assertThat(context)
19+
.hasNotFailed()
20+
.doesNotHaveBean(Container.class)
21+
);
22+
}
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration shutdownHook="disable">
3+
<Appenders>
4+
<Console name="console" target="SYSTEM_OUT" follow="true">
5+
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} |%-5p| %logger{36} - %m%n"/>
6+
</Console>
7+
</Appenders>
8+
<Loggers>
9+
<Logger name="org.testcontainers" level="warn"/>
10+
<Logger name="org.testcontainers.dockerclient" level="info"/>
11+
<Logger name="org.apache.zookeeper" level="info"/>
12+
<Logger name="com.playtika.testcontainer" level="info"/>
13+
<Logger name="org.springframework" level="error"/>
14+
<Root level="error">
15+
<AppenderRef ref="console"/>
16+
</Root>
17+
</Loggers>
18+
</Configuration>

0 commit comments

Comments
 (0)