diff --git a/pom.xml b/pom.xml
index 7bdf6cc..a6574a0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
jar
testcontainers-annotations
- 1.0.5
+ 1.0.6
https://github.com/rbkmoney/testcontainers-annotations
diff --git a/src/main/java/com/rbkmoney/testcontainers/annotations/ceph/CephTestcontainerExtension.java b/src/main/java/com/rbkmoney/testcontainers/annotations/ceph/CephTestcontainerExtension.java
index 9479b96..c00f92e 100644
--- a/src/main/java/com/rbkmoney/testcontainers/annotations/ceph/CephTestcontainerExtension.java
+++ b/src/main/java/com/rbkmoney/testcontainers/annotations/ceph/CephTestcontainerExtension.java
@@ -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),
diff --git a/src/main/java/com/rbkmoney/testcontainers/annotations/ceph/CephTestcontainerFactory.java b/src/main/java/com/rbkmoney/testcontainers/annotations/ceph/CephTestcontainerFactory.java
index 2e2893f..3355030 100644
--- a/src/main/java/com/rbkmoney/testcontainers/annotations/ceph/CephTestcontainerFactory.java
+++ b/src/main/java/com/rbkmoney/testcontainers/annotations/ceph/CephTestcontainerFactory.java
@@ -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();
diff --git a/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainer.java b/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainer.java
new file mode 100644
index 0000000..c626ef6
--- /dev/null
+++ b/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainer.java
@@ -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";
+
+}
diff --git a/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerExtension.java b/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerExtension.java
new file mode 100644
index 0000000..ab0aac6
--- /dev/null
+++ b/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerExtension.java
@@ -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> 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 findMinioTestcontainerAnnotation(ExtensionContext context) {
+ return AnnotationSupport.findAnnotation(context.getElement(), MinioTestcontainer.class);
+ }
+
+ private static Optional findMinioTestcontainerAnnotation(Class> testClass) {
+ return AnnotationSupport.findAnnotation(testClass, MinioTestcontainer.class);
+ }
+
+ private static Optional findMinioTestcontainerSingletonAnnotation(
+ ExtensionContext context) {
+ return AnnotationSupport.findAnnotation(context.getElement(), MinioTestcontainerSingleton.class);
+ }
+
+ private static Optional findMinioTestcontainerSingletonAnnotation(
+ Class> testClass) {
+ return AnnotationSupport.findAnnotation(testClass, MinioTestcontainerSingleton.class);
+ }
+
+ public static class MinioTestcontainerContextCustomizerFactory implements ContextCustomizerFactory {
+
+ @Override
+ public ContextCustomizer createContextCustomizer(
+ Class> testClass,
+ List 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);
+ }
+ }
+}
diff --git a/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerFactory.java b/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerFactory.java
new file mode 100644
index 0000000..06b11d4
--- /dev/null
+++ b/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerFactory.java
@@ -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();
+
+ }
+}
diff --git a/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerSingleton.java b/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerSingleton.java
new file mode 100644
index 0000000..907ee4c
--- /dev/null
+++ b/src/main/java/com/rbkmoney/testcontainers/annotations/minio/MinioTestcontainerSingleton.java
@@ -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";
+
+}
diff --git a/src/main/java/com/rbkmoney/testcontainers/annotations/util/GenericContainerUtil.java b/src/main/java/com/rbkmoney/testcontainers/annotations/util/GenericContainerUtil.java
index 37c7abf..d86ac8b 100644
--- a/src/main/java/com/rbkmoney/testcontainers/annotations/util/GenericContainerUtil.java
+++ b/src/main/java/com/rbkmoney/testcontainers/annotations/util/GenericContainerUtil.java
@@ -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);
+ }
}
diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories
index fe35cd1..39c4e02 100644
--- a/src/main/resources/META-INF/spring.factories
+++ b/src/main/resources/META-INF/spring.factories
@@ -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
diff --git a/src/main/resources/testcontainers-annotations.yml b/src/main/resources/testcontainers-annotations.yml
index 16733ea..45321fb 100644
--- a/src/main/resources/testcontainers-annotations.yml
+++ b/src/main/resources/testcontainers-annotations.yml
@@ -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'