add @CephTestcontainer (#8)

This commit is contained in:
Anatoly Karlov 2021-07-30 12:56:21 +07:00 committed by GitHub
parent 52005f6c32
commit 84f5ed2789
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 298 additions and 12 deletions

View File

@ -12,7 +12,7 @@
<packaging>jar</packaging>
<artifactId>testcontainers-annotations</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
<url>https://github.com/rbkmoney/testcontainers-annotations</url>
<licenses>
@ -36,7 +36,7 @@
</scm>
<properties>
<kafka-common-lib.version>0.1.8</kafka-common-lib.version>
<kafka-common-lib.version>0.1.9</kafka-common-lib.version>
<woody.version>[1.1.22,)</woody.version>
<spring-boot.version>2.5.0</spring-boot.version>
<lombok.version>1.18.16</lombok.version>

View File

@ -0,0 +1,28 @@
package com.rbkmoney.testcontainers.annotations.ceph;
import org.junit.jupiter.api.extension.ExtendWith;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(CephTestcontainerExtension.class)
public @interface CephTestcontainer {
/**
* properties = {"postgresql.make.happy=true",...}
*/
String[] properties() default {};
String signingRegion() default "RU";
String clientProtocol() default "HTTP";
String clientMaxErrorRetry() default "10";
String bucketName() default "TEST";
}

View File

@ -0,0 +1,139 @@
package com.rbkmoney.testcontainers.annotations.ceph;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
import org.junit.platform.commons.support.AnnotationSupport;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.testcontainers.containers.GenericContainer;
import java.util.List;
import java.util.Optional;
import static com.rbkmoney.testcontainers.annotations.ceph.CephTestcontainerFactory.ACCESS_KEY;
import static com.rbkmoney.testcontainers.annotations.ceph.CephTestcontainerFactory.SECRET_KEY;
import static com.rbkmoney.testcontainers.annotations.util.GenericContainerUtil.startContainer;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@Slf4j
public class CephTestcontainerExtension
implements TestInstancePostProcessor, BeforeAllCallback, AfterAllCallback {
private static final ThreadLocal<GenericContainer<?>> THREAD_CONTAINER = new ThreadLocal<>();
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
var annotation = findCephTestcontainerSingletonAnnotation(context);
if (!annotation.isPresent()) {
return;
}
var container = CephTestcontainerFactory.singletonContainer();
if (!container.isRunning()) {
startContainer(container);
}
THREAD_CONTAINER.set(container);
}
@Override
public void beforeAll(ExtensionContext context) {
var annotation = findCephTestcontainerAnnotation(context);
if (!annotation.isPresent()) {
return;
}
var container = CephTestcontainerFactory.container();
if (!container.isRunning()) {
startContainer(container);
}
THREAD_CONTAINER.set(container);
}
@Override
public void afterAll(ExtensionContext context) {
if (findCephTestcontainerAnnotation(context).isPresent()) {
var container = THREAD_CONTAINER.get();
if (container != null && container.isRunning()) {
container.stop();
}
THREAD_CONTAINER.remove();
} else if (findCephTestcontainerSingletonAnnotation(context).isPresent()) {
THREAD_CONTAINER.remove();
}
}
private static Optional<CephTestcontainer> findCephTestcontainerAnnotation(ExtensionContext context) {
return AnnotationSupport.findAnnotation(context.getElement(), CephTestcontainer.class);
}
private static Optional<CephTestcontainer> findCephTestcontainerAnnotation(Class<?> testClass) {
return AnnotationSupport.findAnnotation(testClass, CephTestcontainer.class);
}
private static Optional<CephTestcontainerSingleton> findCephTestcontainerSingletonAnnotation(
ExtensionContext context) {
return AnnotationSupport.findAnnotation(context.getElement(), CephTestcontainerSingleton.class);
}
private static Optional<CephTestcontainerSingleton> findCephTestcontainerSingletonAnnotation(
Class<?> testClass) {
return AnnotationSupport.findAnnotation(testClass, CephTestcontainerSingleton.class);
}
public static class CephTestcontainerContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(
Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
return (context, mergedConfig) -> {
var cephTestcontainerAnnotation = findCephTestcontainerAnnotation(testClass);
if (cephTestcontainerAnnotation.isPresent()) {
var annotation = cephTestcontainerAnnotation.get();
init(
context,
annotation.signingRegion(),
annotation.clientProtocol(),
annotation.clientMaxErrorRetry(),
annotation.bucketName(),
annotation.properties());
} else {
findCephTestcontainerSingletonAnnotation(testClass).ifPresent(
annotation -> init(
context,
annotation.signingRegion(),
annotation.clientProtocol(),
annotation.clientMaxErrorRetry(),
annotation.bucketName(),
annotation.properties()));
}
};
}
private void init(
ConfigurableApplicationContext context,
String signingRegion,
String clientProtocol,
String clientMaxErrorRetry,
String bucketName,
String[] properties) {
var container = THREAD_CONTAINER.get();
TestPropertyValues.of(
"storage.endpoint=" + container.getContainerIpAddress() + ":"
+ container.getMappedPort(8080),
"storage.signingRegion=" + signingRegion,
"storage.accessKey=" + loadDefaultLibraryProperty(ACCESS_KEY),
"storage.secretKey=" + loadDefaultLibraryProperty(SECRET_KEY),
"storage.clientProtocol=" + clientProtocol,
"storage.clientMaxErrorRetry=" + clientMaxErrorRetry,
"storage.bucketName=" + bucketName)
.and(properties)
.applyTo(context);
}
}
}

View File

@ -0,0 +1,81 @@
package com.rbkmoney.testcontainers.annotations.ceph;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.Synchronized;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.utility.DockerImageName;
import java.time.Duration;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CephTestcontainerFactory {
public static final String ACCESS_KEY = "testcontainers.ceph.accessKey";
public static final String SECRET_KEY = "testcontainers.ceph.secretKey";
private static final String CEPH_DAEMON_IMAGE_NAME = "ceph/daemon";
private static final String TAG_PROPERTY = "testcontainers.ceph.tag";
private GenericContainer<?> cephDaemonContainer;
public static GenericContainer<?> container() {
return instance().create();
}
public static GenericContainer<?> singletonContainer() {
return instance().getOrCreateSingletonContainer();
}
private static CephTestcontainerFactory instance() {
return SingletonHolder.INSTANCE;
}
@Synchronized
private GenericContainer<?> getOrCreateSingletonContainer() {
if (cephDaemonContainer != null) {
return cephDaemonContainer;
}
cephDaemonContainer = create();
return cephDaemonContainer;
}
private GenericContainer<?> create() {
try (GenericContainer<?> container = new GenericContainer<>(
DockerImageName
.parse(CEPH_DAEMON_IMAGE_NAME)
.withTag(loadDefaultLibraryProperty(TAG_PROPERTY)))
.withNetworkAliases("ceph-daemon")
.withEnv("RGW_NAME", "localhost")
.withEnv("NETWORK_AUTO_DETECT", "4")
.withEnv("CEPH_DAEMON", "demo")
.withEnv("CEPH_DEMO_UID", "ceph-test")
.withEnv("CEPH_DEMO_ACCESS_KEY", loadDefaultLibraryProperty(ACCESS_KEY))
.withEnv("CEPH_DEMO_SECRET_KEY", loadDefaultLibraryProperty(SECRET_KEY))
.withEnv("CEPH_DEMO_BUCKET", "TEST")
.waitingFor(cephHealthCheck())) {
return container;
}
}
private WaitStrategy cephHealthCheck() {
return getWaitStrategy("/api/v0.1/health", 200, 5000, Duration.ofMinutes(1));
}
private static WaitStrategy getWaitStrategy(String path, Integer statusCode, Integer port, Duration duration) {
return new HttpWaitStrategy()
.forPath(path)
.forPort(port)
.forStatusCode(statusCode)
.withStartupTimeout(duration);
}
private static class SingletonHolder {
private static final CephTestcontainerFactory INSTANCE = new CephTestcontainerFactory();
}
}

View File

@ -0,0 +1,30 @@
package com.rbkmoney.testcontainers.annotations.ceph;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.transaction.annotation.Transactional;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(CephTestcontainerExtension.class)
@Transactional
public @interface CephTestcontainerSingleton {
/**
* properties = {"postgresql.make.happy=true",...}
*/
String[] properties() default {};
String signingRegion() default "RU";
String clientProtocol() default "HTTP";
String clientMaxErrorRetry() default "10";
String bucketName() default "TEST";
}

View File

@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.ClickHouseContainer;
import org.testcontainers.utility.DockerImageName;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadTagFromSpringApplicationPropertiesFile;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@ -43,7 +43,8 @@ public class ClickhouseTestcontainerFactory {
try (ClickHouseContainer container = new ClickHouseContainer(
DockerImageName
.parse(CLICKHOUSE_IMAGE_NAME)
.withTag(loadTagFromSpringApplicationPropertiesFile(TAG_PROPERTY)))) {
.withTag(loadDefaultLibraryProperty(TAG_PROPERTY)))) {
container.withNetworkAliases("clickhouse-server");
return container;
}
}

View File

@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.KafkaContainer;
import org.testcontainers.utility.DockerImageName;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadTagFromSpringApplicationPropertiesFile;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@ -43,8 +43,9 @@ public class KafkaTestcontainerFactory {
try (KafkaContainer container = new KafkaContainer(
DockerImageName
.parse(KAFKA_IMAGE_NAME)
.withTag(loadTagFromSpringApplicationPropertiesFile(TAG_PROPERTY)))
.withTag(loadDefaultLibraryProperty(TAG_PROPERTY)))
.withEmbeddedZookeeper()) {
container.withNetworkAliases("cp-kafka");
return container;
}
}

View File

@ -6,7 +6,7 @@ import lombok.Synchronized;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadTagFromSpringApplicationPropertiesFile;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PostgresqlTestcontainerFactory {
@ -41,7 +41,8 @@ public class PostgresqlTestcontainerFactory {
try (PostgreSQLContainer<?> container = new PostgreSQLContainer<>(
DockerImageName
.parse(POSTGRESQL_IMAGE_NAME)
.withTag(loadTagFromSpringApplicationPropertiesFile(TAG_PROPERTY)))) {
.withTag(loadDefaultLibraryProperty(TAG_PROPERTY)))) {
container.withNetworkAliases("postgres");
return container;
}
}

View File

@ -23,14 +23,14 @@ import static org.assertj.core.api.Assertions.assertThat;
public class SpringApplicationPropertiesLoader {
public static String loadTagFromSpringApplicationPropertiesFile(String keyTag) {
var tag = loadPropertiesByFile().get(keyTag);
public static String loadDefaultLibraryProperty(String key) {
var tag = loadPropertiesByFile().get(key);
if (tag == null) {
tag = getSource(PropertiesFileParameters.builder()
.propertySourceLoader(YamlPropertySourceLoader::new)
.name("testcontainers-annotations.yml")
.build())
.get(keyTag);
.get(key);
}
return String.valueOf(tag);
}

View File

@ -2,4 +2,5 @@
org.springframework.test.context.ContextCustomizerFactory=\
com.rbkmoney.testcontainers.annotations.postgresql.PostgresqlTestcontainerExtension.PostgresqlTestcontainerContextCustomizerFactory,\
com.rbkmoney.testcontainers.annotations.kafka.KafkaTestcontainerExtension.KafkaTestcontainerContextCustomizerFactory,\
com.rbkmoney.testcontainers.annotations.clickhouse.ClickhouseTestcontainerExtension.ClickhouseTestcontainerContextCustomizerFactory
com.rbkmoney.testcontainers.annotations.clickhouse.ClickhouseTestcontainerExtension.ClickhouseTestcontainerContextCustomizerFactory,\
com.rbkmoney.testcontainers.annotations.ceph.CephTestcontainerExtension.CephTestcontainerContextCustomizerFactory

View File

@ -5,3 +5,7 @@ testcontainers:
tag: '6.2.0'
clickhouse:
tag: 'latest-alpine'
ceph:
tag: 'v3.0.5-stable-3.0-luminous-centos-7'
accessKey: 'test'
secretKey: 'test'