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>

Reply via email to