This is an automated email from the ASF dual-hosted git repository. reschke pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/trunk by this push: new ae576b4646 OAK-10464: Use Testcontainers instead of com.arakelian:docker-junit-rule (#1143) ae576b4646 is described below commit ae576b464625c48590108eeb8df9186b713aa234 Author: t-rana <145645280+t-r...@users.noreply.github.com> AuthorDate: Tue Oct 31 19:44:26 2023 +0530 OAK-10464: Use Testcontainers instead of com.arakelian:docker-junit-rule (#1143) * OAK-10464 testcontainers dependency * OAK-10464 testcontainers dependency * modified blob endpoint, logback, add testcontainer dependency * migrate mongo docker rule to testcontainer * minor dependency changes * minor changes * refactor dependency, removed unused ones * revert logback changes * revert logback changes * removed test container dependency from child modules * remove unused dependency --------- Co-authored-by: smiroslav <miros...@apache.org> --- oak-blob-cloud-azure/pom.xml | 5 +- .../cloud/azure/blobstorage/AzuriteDockerRule.java | 128 ++++++++++----------- oak-it/pom.xml | 4 +- oak-jcr/pom.xml | 4 +- oak-lucene/pom.xml | 4 +- oak-parent/pom.xml | 8 +- oak-pojosr/pom.xml | 4 +- oak-run-commons/pom.xml | 4 +- oak-run/pom.xml | 4 +- oak-segment-aws/pom.xml | 5 - oak-segment-azure/pom.xml | 4 +- oak-store-document/pom.xml | 5 - .../plugins/document/MongoConnectionFactory.java | 2 +- .../plugins/document/mongo/MongoDockerRule.java | 124 ++++++++++++-------- oak-upgrade/pom.xml | 8 +- 15 files changed, 161 insertions(+), 152 deletions(-) diff --git a/oak-blob-cloud-azure/pom.xml b/oak-blob-cloud-azure/pom.xml index b7430071a4..5da9251b3d 100644 --- a/oak-blob-cloud-azure/pom.xml +++ b/oak-blob-cloud-azure/pom.xml @@ -198,10 +198,9 @@ <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> diff --git a/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzuriteDockerRule.java b/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzuriteDockerRule.java index f2693ebc05..3eaf27980a 100644 --- a/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzuriteDockerRule.java +++ b/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzuriteDockerRule.java @@ -16,55 +16,88 @@ */ package org.apache.jackrabbit.oak.blob.cloud.azure.blobstorage; -import com.arakelian.docker.junit.DockerRule; -import com.arakelian.docker.junit.model.ImmutableDockerConfig; import com.microsoft.azure.storage.CloudStorageAccount; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.blob.CloudBlobClient; import com.microsoft.azure.storage.blob.CloudBlobContainer; -import com.spotify.docker.client.DefaultDockerClient; -import com.spotify.docker.client.auth.FixedRegistryAuthSupplier; -import java.net.URISyntaxException; -import java.security.InvalidKeyException; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicReference; - -import org.jetbrains.annotations.NotNull; import org.junit.Assume; -import org.junit.rules.TestRule; +import org.junit.rules.ExternalResource; import org.junit.runner.Description; +import org.junit.runners.model.MultipleFailureException; import org.junit.runners.model.Statement; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; -public class AzuriteDockerRule implements TestRule { +import java.net.URISyntaxException; +import java.security.InvalidKeyException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; - private static final String IMAGE = "mcr.microsoft.com/azure-storage/azurite:3.19.0"; +public class AzuriteDockerRule extends ExternalResource { + private static final DockerImageName DOCKER_IMAGE_NAME = DockerImageName.parse("mcr.microsoft.com/azure-storage/azurite:3.19.0"); public static final String ACCOUNT_KEY = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; public static final String ACCOUNT_NAME = "devstoreaccount1"; + private static final AtomicReference<Exception> STARTUP_EXCEPTION = new AtomicReference<>(); - private static final String CONTAINER_SUFFIX = UUID.randomUUID().toString().substring(0, 8); + private GenericContainer<?> azuriteContainer; - private final DockerRule wrappedRule; + @Override + protected void before() throws Throwable { + azuriteContainer = new GenericContainer<>(DOCKER_IMAGE_NAME) + .withExposedPorts(10000) + .withEnv(Map.of("executable", "blob")) + .withStartupTimeout(Duration.ofSeconds(30)); - private static final AtomicReference<Exception> STARTUP_EXCEPTION = new AtomicReference<>(); + try { + azuriteContainer.start(); + } catch (IllegalStateException e) { + STARTUP_EXCEPTION.set(e); + throw e; + } + } - public AzuriteDockerRule() { - wrappedRule = new DockerRule(ImmutableDockerConfig.builder() - .image(IMAGE) - .name("oak-test-azurite-" + CONTAINER_SUFFIX) - .ports("10000") - .addStartedListener(container -> { + @Override + protected void after() { + if (azuriteContainer != null) { + azuriteContainer.stop(); + } + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { try { - container.waitForPort("10000/tcp"); - container.waitForLog("Azurite Blob service is successfully listening at http://0.0.0.0:10000"); + before(); } catch (IllegalStateException e) { - STARTUP_EXCEPTION.set(e); + Assume.assumeNoException(STARTUP_EXCEPTION.get()); throw e; } - }) - .addContainerConfigurer(builder -> builder.env("executable=blob")) - .alwaysRemoveContainer(true) - .build()); + + List<Throwable> errors = new ArrayList<Throwable>(); + try { + base.evaluate(); + } catch (Throwable t) { + errors.add(t); + } finally { + try { + after(); + } catch (Throwable t) { + errors.add(t); + } + } + MultipleFailureException.assertEmpty(errors); + } + }; + } + + public String getBlobEndpoint() { + return "http://127.0.0.1:" + getMappedPort() + "/devstoreaccount1"; } public CloudBlobContainer getContainer(String name) throws URISyntaxException, StorageException, InvalidKeyException { @@ -83,42 +116,7 @@ public class AzuriteDockerRule implements TestRule { return CloudStorageAccount.parse("DefaultEndpointsProtocol=http;" + ";" + accountName + ";" + accountKey + ";" + blobEndpoint); } - @NotNull - public String getBlobEndpoint() { - int mappedPort = getMappedPort(); - return "http://127.0.0.1:" + mappedPort + "/devstoreaccount1"; - } - - @Override - public Statement apply(Statement statement, Description description) { - try { - DefaultDockerClient client = DefaultDockerClient.fromEnv() - .connectTimeoutMillis(5000L) - .readTimeoutMillis(20000L) - .registryAuthSupplier(new FixedRegistryAuthSupplier()) - .build(); - client.ping(); - client.pull(IMAGE); - client.close(); - } catch (Throwable t) { - Assume.assumeNoException(t); - } - - Statement base = wrappedRule.apply(statement, description); - return new Statement() { - @Override - public void evaluate() throws Throwable { - try { - base.evaluate(); - } catch (Throwable e) { - Assume.assumeNoException(STARTUP_EXCEPTION.get()); - throw e; - } - } - }; - } - public int getMappedPort() { - return wrappedRule.getContainer().getPortBinding("10000/tcp").getPort(); + return azuriteContainer.getMappedPort(10000); } } diff --git a/oak-it/pom.xml b/oak-it/pom.xml index 50ebf4a4f0..696e9b7576 100644 --- a/oak-it/pom.xml +++ b/oak-it/pom.xml @@ -258,8 +258,8 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> diff --git a/oak-jcr/pom.xml b/oak-jcr/pom.xml index 47d605cd7f..56f578ce82 100644 --- a/oak-jcr/pom.xml +++ b/oak-jcr/pom.xml @@ -526,8 +526,8 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> </dependencies> diff --git a/oak-lucene/pom.xml b/oak-lucene/pom.xml index a0686bf27d..f9f4c6c46d 100644 --- a/oak-lucene/pom.xml +++ b/oak-lucene/pom.xml @@ -450,8 +450,8 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> diff --git a/oak-parent/pom.xml b/oak-parent/pom.xml index 151391b263..8a9b6c7d43 100644 --- a/oak-parent/pom.xml +++ b/oak-parent/pom.xml @@ -704,10 +704,12 @@ <artifactId>httpmime</artifactId> <version>4.5.14</version> </dependency> + <!-- Testcontainers dependency --> <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> - <version>2.3.0</version> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> + <version>${testcontainers.version}</version> + <scope>test</scope> </dependency> <dependency> <groupId>com.microsoft.azure</groupId> diff --git a/oak-pojosr/pom.xml b/oak-pojosr/pom.xml index 219910c98b..874bbfabcf 100644 --- a/oak-pojosr/pom.xml +++ b/oak-pojosr/pom.xml @@ -367,8 +367,8 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> </dependencies> diff --git a/oak-run-commons/pom.xml b/oak-run-commons/pom.xml index 8e477bcfb7..e4495a6b72 100644 --- a/oak-run-commons/pom.xml +++ b/oak-run-commons/pom.xml @@ -199,8 +199,8 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> diff --git a/oak-run/pom.xml b/oak-run/pom.xml index a82501e5a1..8f36fd848e 100644 --- a/oak-run/pom.xml +++ b/oak-run/pom.xml @@ -474,8 +474,8 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> diff --git a/oak-segment-aws/pom.xml b/oak-segment-aws/pom.xml index 9a4a2c5698..783294774e 100644 --- a/oak-segment-aws/pom.xml +++ b/oak-segment-aws/pom.xml @@ -257,11 +257,6 @@ <artifactId>logback-classic</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>io.findify</groupId> <artifactId>s3mock_2.12</artifactId> diff --git a/oak-segment-azure/pom.xml b/oak-segment-azure/pom.xml index 813c13791f..f150efe96b 100644 --- a/oak-segment-azure/pom.xml +++ b/oak-segment-azure/pom.xml @@ -208,8 +208,8 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> diff --git a/oak-store-document/pom.xml b/oak-store-document/pom.xml index 8a26e4dc83..6b0a37f921 100644 --- a/oak-store-document/pom.xml +++ b/oak-store-document/pom.xml @@ -331,11 +331,6 @@ <artifactId>metrics-core</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoConnectionFactory.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoConnectionFactory.java index 128df92c1a..ce07750d65 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoConnectionFactory.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoConnectionFactory.java @@ -55,7 +55,7 @@ public class MongoConnectionFactory extends ExternalResource { MongoConnection c = MongoUtils.getConnection(dbName); if (c == null && MongoDockerRule.isDockerAvailable()) { // fall back to docker if available - c = new MongoConnection("localhost", mongo.getPort(), dbName); + c = new MongoConnection(mongo.getHost(), mongo.getPort(), dbName); } assumeNotNull(c); if (c != null) { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDockerRule.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDockerRule.java index 114755ccb6..c7d204b69d 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDockerRule.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDockerRule.java @@ -16,95 +16,117 @@ */ package org.apache.jackrabbit.oak.plugins.document.mongo; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicReference; - -import com.arakelian.docker.junit.DockerRule; -import com.arakelian.docker.junit.model.ImmutableDockerConfig; -import com.spotify.docker.client.DefaultDockerClient; -import com.spotify.docker.client.auth.FixedRegistryAuthSupplier; - import org.junit.Assume; -import org.junit.rules.TestRule; +import org.junit.rules.ExternalResource; import org.junit.runner.Description; +import org.junit.runners.model.MultipleFailureException; import org.junit.runners.model.Statement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.RemoteDockerImage; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; /** - * A MongoDB {@link DockerRule}. + * A MongoDB {@link GenericContainer}. */ -public class MongoDockerRule implements TestRule { +public class MongoDockerRule extends ExternalResource { private static final Logger LOG = LoggerFactory.getLogger(MongoDockerRule.class); - private static final String CONFIG_NAME = "MongoDB-" + UUID.randomUUID().toString().substring(0, 8); - private static final String VERSION = System.getProperty("mongo.version", "3.6"); - - private static final String IMAGE = "mongo:" + VERSION; - + private static final String MONGO_IMAGE = "mongo:" + VERSION; + private static final AtomicReference<Exception> STARTUP_EXCEPTION = new AtomicReference<>(); + private static final int DEFAULT_MONGO_PORT = 27017; + private static final DockerImageName DOCKER_IMAGE_NAME = DockerImageName.parse(MONGO_IMAGE); private static final boolean DOCKER_AVAILABLE; + private static GenericContainer<?> mongoContainer; static { - boolean available = false; - try (DefaultDockerClient client = DefaultDockerClient.fromEnv() - .connectTimeoutMillis(5000L).readTimeoutMillis(20000L) - .registryAuthSupplier(new FixedRegistryAuthSupplier()) - .build()) { - client.ping(); - client.pull(IMAGE); - available = true; + + boolean dockerAvailable = false; + boolean imageAvailable = false; + try { + dockerAvailable = checkDockerAvailability(); + if (dockerAvailable) { + imageAvailable = checkImageAvailability(); + } else { + LOG.info("docker not available"); + } } catch (Throwable t) { - LOG.info("Cannot connect to docker or pull image", t); + LOG.error("not able to pull specified mongo image: {}, error: ", MONGO_IMAGE, t); } - DOCKER_AVAILABLE = available; + DOCKER_AVAILABLE = dockerAvailable && imageAvailable; } - private final DockerRule wrappedRule; - - private static final AtomicReference<Exception> STARTUP_EXCEPTION = new AtomicReference<>(); - - public MongoDockerRule() { - wrappedRule = new DockerRule(ImmutableDockerConfig.builder() - .name(CONFIG_NAME) - .image(IMAGE) - .ports("27017") - .allowRunningBetweenUnitTests(true) - .alwaysRemoveContainer(true) - .addStartedListener(container -> { - try { - container.waitForPort("27017/tcp"); - } catch (IllegalStateException e) { - STARTUP_EXCEPTION.set(e); - throw e; - } - }) - .build()); + @Override + protected void before() throws Throwable { + if (mongoContainer != null && mongoContainer.isRunning()) { + return; + } + mongoContainer = new GenericContainer<>(DOCKER_IMAGE_NAME) + .withExposedPorts(DEFAULT_MONGO_PORT) + .withStartupTimeout(Duration.ofMinutes(1)); + + try { + long startTime = Instant.now().toEpochMilli(); + mongoContainer.start(); + LOG.info("mongo container started in: " + (Instant.now().toEpochMilli() - startTime) + " ms"); + } catch (Exception e) { + LOG.error("error while starting mongoDb container, error: ", e); + STARTUP_EXCEPTION.set(e); + throw e; + } } @Override - public Statement apply(Statement statement, Description description) { - Statement base = wrappedRule.apply(statement, description); + public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { try { - base.evaluate(); + before(); } catch (Throwable e) { Assume.assumeNoException(STARTUP_EXCEPTION.get()); throw e; } + + List<Throwable> errors = new ArrayList<>(); + try { + base.evaluate(); + } catch (Throwable t) { + errors.add(t); + } + MultipleFailureException.assertEmpty(errors); } }; } + private static boolean checkImageAvailability() throws TimeoutException { + RemoteDockerImage remoteDockerImage = new RemoteDockerImage(DOCKER_IMAGE_NAME); + remoteDockerImage.get(1, TimeUnit.MINUTES); + return true; + } + + private static boolean checkDockerAvailability() { + return DockerClientFactory.instance().isDockerAvailable(); + } + public int getPort() { - return wrappedRule.getContainer().getPortBinding("27017/tcp").getPort(); + return mongoContainer.getMappedPort(DEFAULT_MONGO_PORT); } public String getHost() { - return wrappedRule.getContainer().getPortBinding("27017/tcp").getHost(); + return mongoContainer.getHost(); } public static boolean isDockerAvailable() { diff --git a/oak-upgrade/pom.xml b/oak-upgrade/pom.xml index 731232ba8b..3cc700ee85 100644 --- a/oak-upgrade/pom.xml +++ b/oak-upgrade/pom.xml @@ -220,11 +220,9 @@ <type>test-jar</type> <scope>test</scope> </dependency> - - <dependency> - <groupId>com.arakelian</groupId> - <artifactId>docker-junit-rule</artifactId> - <version>2.1.0</version> + <dependency> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> <dependency>