add @OpensearchTestcontainer (#28)

This commit is contained in:
Anatolii Karlov 2023-11-27 17:21:22 +02:00 committed by GitHub
parent 8ff2f71c11
commit 5888ac5a0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 227 additions and 15 deletions

View File

@ -17,17 +17,19 @@ confluentinc/cp-kafka
yandex/clickhouse-server
ceph/daemon
minio/minio
opensearchproject/opensearch
```
Базовые аннотации для использования:
```java
@PostgresqlTestcontainer /@PostgresqlTestcontainerSingleton
@KafkaTestcontainer /@KafkaTestcontainerSingleton
@ClickhouseTestcontainer /@ClickhouseTestcontainerSingleton
@CephTestcontainer /@CephTestcontainerSingleton
@MinioTestcontainer /@MinioTestcontainerSingleton
```
| basic | singleton |
|--------------------------|-----------------------------------|
| @PostgresqlTestcontainer | @PostgresqlTestcontainerSingleton |
| @KafkaTestcontainer | @KafkaTestcontainerSingleton |
| @ClickhouseTestcontainer | @ClickhouseTestcontainerSingleton |
| @CephTestcontainer | @CephTestcontainerSingleton |
| @MinioTestcontainer | @MinioTestcontainerSingleton |
| @OpensearchTestcontainer | @OpensearchTestcontainerSingleton |
Для изменения `docker image tag`, который используется тестконтейнерами нужно переопределить параметры в `application.yml`:
@ -47,6 +49,8 @@ testcontainers:
tag: 'RELEASE.2021-10-13T00-23-17Z'
user: 'minio'
password: 'minio123'
opensearch:
tag: '2.0.0'
```
Eсли параметр не указан библиотека будет использовать параметры по умолчанию, указанные в репозитории в

View File

@ -6,13 +6,13 @@
<parent>
<groupId>dev.vality</groupId>
<artifactId>library-parent-pom</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</parent>
<packaging>jar</packaging>
<artifactId>testcontainers-annotations</artifactId>
<version>2.0.2</version>
<version>2.0.3</version>
<name>testcontainers-annotations</name>
<description>testcontainers-annotations</description>

View File

@ -0,0 +1,23 @@
package dev.vality.testcontainers.annotations.opensearch;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
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(OpensearchTestcontainerExtension.class)
public @interface OpensearchTestcontainer {
/**
* Аналогичный параметр как у аннотации {@link SpringBootTest#properties()}
* <p>
* пример properties = {"opensearch.make.happy=true",...}
*/
String[] properties() default {};
}

View File

@ -0,0 +1,92 @@
package dev.vality.testcontainers.annotations.opensearch;
import dev.vality.testcontainers.annotations.util.GenericContainerUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
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;
@Slf4j
public class OpensearchTestcontainerExtension implements BeforeAllCallback, AfterAllCallback {
private static final ThreadLocal<GenericContainer<?>> THREAD_CONTAINER = new ThreadLocal<>();
@Override
public void beforeAll(ExtensionContext context) {
if (findPrototypeAnnotation(context).isPresent()) {
var container = OpensearchTestcontainerFactory.container();
GenericContainerUtil.startContainer(container);
THREAD_CONTAINER.set(container);
} else if (findSingletonAnnotation(context).isPresent()) {
var container = OpensearchTestcontainerFactory.singletonContainer();
if (!container.isRunning()) {
GenericContainerUtil.startContainer(container);
}
THREAD_CONTAINER.set(container);
}
}
@Override
public void afterAll(ExtensionContext context) {
if (findPrototypeAnnotation(context).isPresent()) {
var container = THREAD_CONTAINER.get();
if (container != null && container.isRunning()) {
container.stop();
}
THREAD_CONTAINER.remove();
} else if (findSingletonAnnotation(context).isPresent()) {
THREAD_CONTAINER.remove();
}
}
private static Optional<OpensearchTestcontainer> findPrototypeAnnotation(ExtensionContext context) {
return AnnotationSupport.findAnnotation(context.getElement(), OpensearchTestcontainer.class);
}
private static Optional<OpensearchTestcontainer> findPrototypeAnnotation(Class<?> testClass) {
return AnnotationSupport.findAnnotation(testClass, OpensearchTestcontainer.class);
}
private static Optional<OpensearchTestcontainerSingleton> findSingletonAnnotation(ExtensionContext context) {
return AnnotationSupport.findAnnotation(context.getElement(), OpensearchTestcontainerSingleton.class);
}
private static Optional<OpensearchTestcontainerSingleton> findSingletonAnnotation(Class<?> testClass) {
return AnnotationSupport.findAnnotation(testClass, OpensearchTestcontainerSingleton.class);
}
public static class OpensearchTestcontainerContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(
Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
return (context, mergedConfig) -> {
if (findPrototypeAnnotation(testClass).isPresent()) {
init(context, findPrototypeAnnotation(testClass).get().properties()); //NOSONAR
} else if (findSingletonAnnotation(testClass).isPresent()) {
init(context, findSingletonAnnotation(testClass).get().properties()); //NOSONAR
}
};
}
private void init(ConfigurableApplicationContext context, String[] properties) {
var container = THREAD_CONTAINER.get();
TestPropertyValues.of(
"opensearch.hostname=" + container.getHost(),
"opensearch.port=" + container.getFirstMappedPort())
.and(properties)
.applyTo(context);
}
}
}

View File

@ -0,0 +1,65 @@
package dev.vality.testcontainers.annotations.opensearch;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.Synchronized;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.utility.DockerImageName;
import java.util.UUID;
import static dev.vality.testcontainers.annotations.util.SpringApplicationPropertiesLoader.loadDefaultLibraryProperty;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class OpensearchTestcontainerFactory {
private static final String OPENSEARCH_IMAGE_NAME = "opensearchproject/opensearch";
private static final String TAG_PROPERTY = "testcontainers.opensearch.tag";
private GenericContainer<?> opensearchContainer;
public static GenericContainer<?> container() {
return instance().create();
}
public static GenericContainer<?> singletonContainer() {
return instance().getOrCreateSingletonContainer();
}
private static OpensearchTestcontainerFactory instance() {
return SingletonHolder.INSTANCE;
}
@Synchronized
private GenericContainer<?> getOrCreateSingletonContainer() {
if (opensearchContainer != null) {
return opensearchContainer;
}
opensearchContainer = create();
return opensearchContainer;
}
private GenericContainer<?> create() {
try (GenericContainer<?> container = new GenericContainer<>(
DockerImageName
.parse(OPENSEARCH_IMAGE_NAME)
.withTag(loadDefaultLibraryProperty(TAG_PROPERTY)))) {
container.withNetworkAliases("opensearch-" + UUID.randomUUID());
container.withExposedPorts(9200, 9600);
container.setWaitStrategy((new HttpWaitStrategy())
.forPort(9200)
.forStatusCodeMatching(response -> response == 200 || response == 401));
container.withEnv("discovery.type", "single-node");
container.withEnv("DISABLE_INSTALL_DEMO_CONFIG", "true");
container.withEnv("DISABLE_SECURITY_PLUGIN", "true");
return container;
}
}
private static class SingletonHolder {
private static final OpensearchTestcontainerFactory INSTANCE = new OpensearchTestcontainerFactory();
}
}

View File

@ -0,0 +1,25 @@
package dev.vality.testcontainers.annotations.opensearch;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
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(OpensearchTestcontainerExtension.class)
@Transactional
public @interface OpensearchTestcontainerSingleton {
/**
* Аналогичный параметр как у аннотации {@link SpringBootTest#properties()}
* <p>
* пример properties = {"opensearch.make.happy=true",...}
*/
String[] properties() default {};
}

View File

@ -23,7 +23,7 @@ public class PostgresqlTestcontainerFactory {
private static final String POSTGRESQL_IMAGE_NAME = "postgres";
private static final String TAG_PROPERTY = "testcontainers.postgresql.tag";
private PostgreSQLContainer<?> postgreSqlContainer;
private PostgreSQLContainer<?> postgresqlContainer;
public static PostgreSQLContainer<?> container() {
return instance().create();
@ -39,11 +39,11 @@ public class PostgresqlTestcontainerFactory {
@Synchronized
private PostgreSQLContainer<?> getOrCreateSingletonContainer() {
if (postgreSqlContainer != null) {
return postgreSqlContainer;
if (postgresqlContainer != null) {
return postgresqlContainer;
}
postgreSqlContainer = create();
return postgreSqlContainer;
postgresqlContainer = create();
return postgresqlContainer;
}
private PostgreSQLContainer<?> create() {

View File

@ -4,4 +4,5 @@ dev.vality.testcontainers.annotations.postgresql.PostgresqlTestcontainerExtensio
dev.vality.testcontainers.annotations.kafka.KafkaTestcontainerExtension.KafkaTestcontainerContextCustomizerFactory,\
dev.vality.testcontainers.annotations.clickhouse.ClickhouseTestcontainerExtension.ClickhouseTestcontainerContextCustomizerFactory,\
dev.vality.testcontainers.annotations.ceph.CephTestcontainerExtension.CephTestcontainerContextCustomizerFactory,\
dev.vality.testcontainers.annotations.minio.MinioTestcontainerExtension.MinioTestcontainerContextCustomizerFactory
dev.vality.testcontainers.annotations.minio.MinioTestcontainerExtension.MinioTestcontainerContextCustomizerFactory,\
dev.vality.testcontainers.annotations.opensearch.OpensearchTestcontainerExtension.OpensearchTestcontainerContextCustomizerFactory

View File

@ -13,3 +13,5 @@ testcontainers:
tag: 'RELEASE.2021-10-13T00-23-17Z'
user: 'minio'
password: 'minio123'
opensearch:
tag: '2.0.0'