add @MinioTestcontainer (#9)

add @MinioTestcontainer #9
This commit is contained in:
Anatoly Karlov 2021-07-30 13:18:18 +07:00 committed by GitHub
parent 84f5ed2789
commit 8b95a81424
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 282 additions and 19 deletions

View File

@ -12,7 +12,7 @@
<packaging>jar</packaging>
<artifactId>testcontainers-annotations</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
<url>https://github.com/rbkmoney/testcontainers-annotations</url>
<licenses>

View File

@ -124,8 +124,8 @@ public class CephTestcontainerExtension
String[] properties) {
var container = THREAD_CONTAINER.get();
TestPropertyValues.of(
"storage.endpoint=" + container.getContainerIpAddress() + ":"
+ container.getMappedPort(8080),
"storage.endpoint=" + container.getContainerIpAddress() + ":" +
container.getMappedPort(8080),
"storage.signingRegion=" + signingRegion,
"storage.accessKey=" + loadDefaultLibraryProperty(ACCESS_KEY),
"storage.secretKey=" + loadDefaultLibraryProperty(SECRET_KEY),

View File

@ -4,12 +4,11 @@ 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.GenericContainerUtil.getWaitStrategy;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@ -56,23 +55,11 @@ public class CephTestcontainerFactory {
.withEnv("CEPH_DEMO_ACCESS_KEY", loadDefaultLibraryProperty(ACCESS_KEY))
.withEnv("CEPH_DEMO_SECRET_KEY", loadDefaultLibraryProperty(SECRET_KEY))
.withEnv("CEPH_DEMO_BUCKET", "TEST")
.waitingFor(cephHealthCheck())) {
.waitingFor(getWaitStrategy("/api/v0.1/health", 200, 5000, Duration.ofMinutes(1)))) {
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,28 @@
package com.rbkmoney.testcontainers.annotations.minio;
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(MinioTestcontainerExtension.class)
public @interface MinioTestcontainer {
/**
* 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.minio;
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.minio.MinioTestcontainerFactory.MINIO_PASSWORD;
import static com.rbkmoney.testcontainers.annotations.minio.MinioTestcontainerFactory.MINIO_USER;
import static com.rbkmoney.testcontainers.annotations.util.GenericContainerUtil.startContainer;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@Slf4j
public class MinioTestcontainerExtension
implements TestInstancePostProcessor, BeforeAllCallback, AfterAllCallback {
private static final ThreadLocal<GenericContainer<?>> THREAD_CONTAINER = new ThreadLocal<>();
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
var annotation = findMinioTestcontainerSingletonAnnotation(context);
if (!annotation.isPresent()) {
return;
}
var container = MinioTestcontainerFactory.singletonContainer();
if (!container.isRunning()) {
startContainer(container);
}
THREAD_CONTAINER.set(container);
}
@Override
public void beforeAll(ExtensionContext context) {
var annotation = findMinioTestcontainerAnnotation(context);
if (!annotation.isPresent()) {
return;
}
var container = MinioTestcontainerFactory.container();
if (!container.isRunning()) {
startContainer(container);
}
THREAD_CONTAINER.set(container);
}
@Override
public void afterAll(ExtensionContext context) {
if (findMinioTestcontainerAnnotation(context).isPresent()) {
var container = THREAD_CONTAINER.get();
if (container != null && container.isRunning()) {
container.stop();
}
THREAD_CONTAINER.remove();
} else if (findMinioTestcontainerSingletonAnnotation(context).isPresent()) {
THREAD_CONTAINER.remove();
}
}
private static Optional<MinioTestcontainer> findMinioTestcontainerAnnotation(ExtensionContext context) {
return AnnotationSupport.findAnnotation(context.getElement(), MinioTestcontainer.class);
}
private static Optional<MinioTestcontainer> findMinioTestcontainerAnnotation(Class<?> testClass) {
return AnnotationSupport.findAnnotation(testClass, MinioTestcontainer.class);
}
private static Optional<MinioTestcontainerSingleton> findMinioTestcontainerSingletonAnnotation(
ExtensionContext context) {
return AnnotationSupport.findAnnotation(context.getElement(), MinioTestcontainerSingleton.class);
}
private static Optional<MinioTestcontainerSingleton> findMinioTestcontainerSingletonAnnotation(
Class<?> testClass) {
return AnnotationSupport.findAnnotation(testClass, MinioTestcontainerSingleton.class);
}
public static class MinioTestcontainerContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(
Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
return (context, mergedConfig) -> {
var minioTestcontainerAnnotation = findMinioTestcontainerAnnotation(testClass);
if (minioTestcontainerAnnotation.isPresent()) {
var annotation = minioTestcontainerAnnotation.get();
init(
context,
annotation.signingRegion(),
annotation.clientProtocol(),
annotation.clientMaxErrorRetry(),
annotation.bucketName(),
annotation.properties());
} else {
findMinioTestcontainerSingletonAnnotation(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(9000),
"storage.signingRegion=" + signingRegion,
"storage.accessKey=" + loadDefaultLibraryProperty(MINIO_USER),
"storage.secretKey=" + loadDefaultLibraryProperty(MINIO_PASSWORD),
"storage.clientProtocol=" + clientProtocol,
"storage.clientMaxErrorRetry=" + clientMaxErrorRetry,
"storage.bucketName=" + bucketName)
.and(properties)
.applyTo(context);
}
}
}

View File

@ -0,0 +1,63 @@
package com.rbkmoney.testcontainers.annotations.minio;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.Synchronized;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
import java.time.Duration;
import static com.rbkmoney.testcontainers.annotations.util.GenericContainerUtil.getWaitStrategy;
import static com.rbkmoney.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MinioTestcontainerFactory {
public static final String MINIO_USER = "testcontainers.minio.user";
public static final String MINIO_PASSWORD = "testcontainers.minio.password";
private static final String MINIO_IMAGE_NAME = "minio/minio";
private static final String TAG_PROPERTY = "testcontainers.minio.tag";
private GenericContainer<?> minioContainer;
public static GenericContainer<?> container() {
return instance().create();
}
public static GenericContainer<?> singletonContainer() {
return instance().getOrCreateSingletonContainer();
}
private static MinioTestcontainerFactory instance() {
return SingletonHolder.INSTANCE;
}
@Synchronized
private GenericContainer<?> getOrCreateSingletonContainer() {
if (minioContainer != null) {
return minioContainer;
}
minioContainer = create();
return minioContainer;
}
private GenericContainer<?> create() {
try (GenericContainer<?> container = new GenericContainer<>(
DockerImageName
.parse(MINIO_IMAGE_NAME)
.withTag(loadDefaultLibraryProperty(TAG_PROPERTY)))
.withNetworkAliases("minio")
.withEnv("MINIO_ROOT_USER", loadDefaultLibraryProperty(MINIO_USER))
.withEnv("MINIO_ROOT_PASSWORD", loadDefaultLibraryProperty(MINIO_PASSWORD))
.waitingFor(getWaitStrategy("/minio/health/live", 200, 9000, Duration.ofMinutes(1)))) {
return container;
}
}
private static class SingletonHolder {
private static final MinioTestcontainerFactory INSTANCE = new MinioTestcontainerFactory();
}
}

View File

@ -0,0 +1,30 @@
package com.rbkmoney.testcontainers.annotations.minio;
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(MinioTestcontainerExtension.class)
@Transactional
public @interface MinioTestcontainerSingleton {
/**
* 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

@ -1,8 +1,11 @@
package com.rbkmoney.testcontainers.annotations.util;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.lifecycle.Startables;
import java.time.Duration;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
@ -15,4 +18,12 @@ public class GenericContainerUtil {
assertThat(container.isRunning())
.isTrue();
}
public static WaitStrategy getWaitStrategy(String path, Integer statusCode, Integer port, Duration duration) {
return new HttpWaitStrategy()
.forPath(path)
.forPort(port)
.forStatusCode(statusCode)
.withStartupTimeout(duration);
}
}

View File

@ -3,4 +3,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.ceph.CephTestcontainerExtension.CephTestcontainerContextCustomizerFactory
com.rbkmoney.testcontainers.annotations.ceph.CephTestcontainerExtension.CephTestcontainerContextCustomizerFactory,\
com.rbkmoney.testcontainers.annotations.minio.MinioTestcontainerExtension.MinioTestcontainerContextCustomizerFactory

View File

@ -9,3 +9,7 @@ testcontainers:
tag: 'v3.0.5-stable-3.0-luminous-centos-7'
accessKey: 'test'
secretKey: 'test'
minio:
tag: 'latest'
user: 'user'
password: 'password'